naga/valid/
interface.rs

1use alloc::vec::Vec;
2
3use bit_set::BitSet;
4
5use super::{
6    analyzer::{FunctionInfo, GlobalUse},
7    Capabilities, Disalignment, FunctionError, ImmediateError, ModuleInfo,
8};
9use crate::arena::{Handle, UniqueArena};
10use crate::span::{AddSpan as _, MapErrWithSpan as _, SpanProvider as _, WithSpan};
11
12const MAX_WORKGROUP_SIZE: u32 = 0x4000;
13
14#[derive(Clone, Debug, thiserror::Error)]
15#[cfg_attr(test, derive(PartialEq))]
16pub enum GlobalVariableError {
17    #[error("Usage isn't compatible with address space {0:?}")]
18    InvalidUsage(crate::AddressSpace),
19    #[error("Type isn't compatible with address space {0:?}")]
20    InvalidType(crate::AddressSpace),
21    #[error("Type {0:?} isn't compatible with binding arrays")]
22    InvalidBindingArray(Handle<crate::Type>),
23    #[error("Type flags {seen:?} do not meet the required {required:?}")]
24    MissingTypeFlags {
25        required: super::TypeFlags,
26        seen: super::TypeFlags,
27    },
28    #[error("Capability {0:?} is not supported")]
29    UnsupportedCapability(Capabilities),
30    #[error("Binding decoration is missing or not applicable")]
31    InvalidBinding,
32    #[error("Alignment requirements for address space {0:?} are not met by {1:?}")]
33    Alignment(
34        crate::AddressSpace,
35        Handle<crate::Type>,
36        #[source] Disalignment,
37    ),
38    #[error("Initializer must be an override-expression")]
39    InitializerExprType,
40    #[error("Initializer doesn't match the variable type")]
41    InitializerType,
42    #[error("Initializer can't be used with address space {0:?}")]
43    InitializerNotAllowed(crate::AddressSpace),
44    #[error("Storage address space doesn't support write-only access")]
45    StorageAddressSpaceWriteOnlyNotSupported,
46    #[error("Type is not valid for use as a immediate data")]
47    InvalidImmediateType(#[source] ImmediateError),
48    #[error("Task payload must not be zero-sized")]
49    ZeroSizedTaskPayload,
50}
51
52#[derive(Clone, Debug, thiserror::Error)]
53#[cfg_attr(test, derive(PartialEq))]
54pub enum VaryingError {
55    #[error("The type {0:?} does not match the varying")]
56    InvalidType(Handle<crate::Type>),
57    #[error("The type {0:?} cannot be used for user-defined entry point inputs or outputs")]
58    NotIOShareableType(Handle<crate::Type>),
59    #[error("Interpolation is not valid")]
60    InvalidInterpolation,
61    #[error("Cannot combine {interpolation:?} interpolation with the {sampling:?} sample type")]
62    InvalidInterpolationSamplingCombination {
63        interpolation: crate::Interpolation,
64        sampling: crate::Sampling,
65    },
66    #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")]
67    MissingInterpolation,
68    #[error("Built-in {0:?} is not available at this stage")]
69    InvalidBuiltInStage(crate::BuiltIn),
70    #[error("Built-in type for {0:?} is invalid. Found {1:?}")]
71    InvalidBuiltInType(crate::BuiltIn, crate::TypeInner),
72    #[error("Entry point arguments and return values must all have bindings")]
73    MissingBinding,
74    #[error("Struct member {0} is missing a binding")]
75    MemberMissingBinding(u32),
76    #[error("Multiple bindings at location {location} are present")]
77    BindingCollision { location: u32 },
78    #[error("Multiple bindings use the same `blend_src` {blend_src}")]
79    BindingCollisionBlendSrc { blend_src: u32 },
80    #[error("Built-in {0:?} is present more than once")]
81    DuplicateBuiltIn(crate::BuiltIn),
82    #[error("Capability {0:?} is not supported")]
83    UnsupportedCapability(Capabilities),
84    #[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
85    InvalidInputAttributeInStage(&'static str, crate::ShaderStage),
86    #[error("The attribute {0:?} is not valid for stage {1:?}")]
87    InvalidAttributeInStage(&'static str, crate::ShaderStage),
88    #[error("The `blend_src` attribute can only be used on location 0, only indices 0 and 1 are valid. Location was {location}, index was {blend_src}.")]
89    InvalidBlendSrcIndex { location: u32, blend_src: u32 },
90    #[error("If `blend_src` is used, there must be exactly two outputs both with location 0, one with `blend_src(0)` and the other with `blend_src(1)`.")]
91    IncompleteBlendSrcUsage,
92    #[error("If `blend_src` is used, both outputs must have the same type. `blend_src(0)` has type {blend_src_0_type:?} and `blend_src(1)` has type {blend_src_1_type:?}.")]
93    BlendSrcOutputTypeMismatch {
94        blend_src_0_type: Handle<crate::Type>,
95        blend_src_1_type: Handle<crate::Type>,
96    },
97    #[error("Workgroup size is multi dimensional, `@builtin(subgroup_id)` and `@builtin(subgroup_invocation_id)` are not supported.")]
98    InvalidMultiDimensionalSubgroupBuiltIn,
99    #[error("The `@per_primitive` attribute can only be used in fragment shader inputs or mesh shader primitive outputs")]
100    InvalidPerPrimitive,
101    #[error("Non-builtin members of a mesh primitive output struct must be decorated with `@per_primitive`")]
102    MissingPerPrimitive,
103}
104
105#[derive(Clone, Debug, thiserror::Error)]
106#[cfg_attr(test, derive(PartialEq))]
107pub enum EntryPointError {
108    #[error("Multiple conflicting entry points")]
109    Conflict,
110    #[error("Vertex shaders must return a `@builtin(position)` output value")]
111    MissingVertexOutputPosition,
112    #[error("Early depth test is not applicable")]
113    UnexpectedEarlyDepthTest,
114    #[error("Workgroup size is not applicable")]
115    UnexpectedWorkgroupSize,
116    #[error("Workgroup size is out of range")]
117    OutOfRangeWorkgroupSize,
118    #[error("Uses operations forbidden at this stage")]
119    ForbiddenStageOperations,
120    #[error("Global variable {0:?} is used incorrectly as {1:?}")]
121    InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse),
122    #[error("More than 1 immediate data variable is used")]
123    MoreThanOneImmediateUsed,
124    #[error("Bindings for {0:?} conflict with other resource")]
125    BindingCollision(Handle<crate::GlobalVariable>),
126    #[error("Argument {0} varying error")]
127    Argument(u32, #[source] VaryingError),
128    #[error(transparent)]
129    Result(#[from] VaryingError),
130    #[error("Location {location} interpolation of an integer has to be flat")]
131    InvalidIntegerInterpolation { location: u32 },
132    #[error(transparent)]
133    Function(#[from] FunctionError),
134    #[error("Capability {0:?} is not supported")]
135    UnsupportedCapability(Capabilities),
136
137    #[error("mesh shader entry point missing mesh shader attributes")]
138    ExpectedMeshShaderAttributes,
139    #[error("Non mesh shader entry point cannot have mesh shader attributes")]
140    UnexpectedMeshShaderAttributes,
141    #[error("Non mesh/task shader entry point cannot have task payload attribute")]
142    UnexpectedTaskPayload,
143    #[error("Task payload must be declared with `var<task_payload>`")]
144    TaskPayloadWrongAddressSpace,
145    #[error("For a task payload to be used, it must be declared with @payload")]
146    WrongTaskPayloadUsed,
147    #[error("Task shader entry point must return @builtin(mesh_task_size) vec3<u32>")]
148    WrongTaskShaderEntryResult,
149    #[error("Task shaders must declare a task payload output")]
150    ExpectedTaskPayload,
151    #[error(
152        "Mesh shader output variable must be a struct with fields that are all allowed builtins"
153    )]
154    BadMeshOutputVariableType,
155    #[error("Mesh shader output variable fields must have types that are in accordance with the mesh shader spec")]
156    BadMeshOutputVariableField,
157    #[error("Mesh shader entry point cannot have a return type")]
158    UnexpectedMeshShaderEntryResult,
159    #[error(
160        "Mesh output type must be a user-defined struct with fields in alignment with the mesh shader spec"
161    )]
162    InvalidMeshOutputType,
163    #[error("Mesh primitive outputs must have exactly one of `@builtin(triangle_indices)`, `@builtin(line_indices)`, or `@builtin(point_index)`")]
164    InvalidMeshPrimitiveOutputType,
165    #[error("Mesh output global variable must live in the workgroup address space")]
166    WrongMeshOutputAddressSpace,
167    #[error("Task payload must be at least 4 bytes, but is {0} bytes")]
168    TaskPayloadTooSmall(u32),
169}
170
171fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
172    let mut storage_usage = GlobalUse::QUERY;
173    if access.contains(crate::StorageAccess::LOAD) {
174        storage_usage |= GlobalUse::READ;
175    }
176    if access.contains(crate::StorageAccess::STORE) {
177        storage_usage |= GlobalUse::WRITE;
178    }
179    if access.contains(crate::StorageAccess::ATOMIC) {
180        storage_usage |= GlobalUse::ATOMIC;
181    }
182    storage_usage
183}
184
185#[derive(Clone, Copy, Debug, PartialEq, Eq)]
186enum MeshOutputType {
187    None,
188    VertexOutput,
189    PrimitiveOutput,
190}
191
192struct VaryingContext<'a> {
193    stage: crate::ShaderStage,
194    output: bool,
195    types: &'a UniqueArena<crate::Type>,
196    type_info: &'a Vec<super::r#type::TypeInfo>,
197    location_mask: &'a mut BitSet,
198    blend_src_mask: &'a mut BitSet,
199    built_ins: &'a mut crate::FastHashSet<crate::BuiltIn>,
200    capabilities: Capabilities,
201    flags: super::ValidationFlags,
202    mesh_output_type: MeshOutputType,
203    has_task_payload: bool,
204}
205
206impl VaryingContext<'_> {
207    fn validate_impl(
208        &mut self,
209        ep: &crate::EntryPoint,
210        ty: Handle<crate::Type>,
211        binding: &crate::Binding,
212    ) -> Result<(), VaryingError> {
213        use crate::{BuiltIn as Bi, ShaderStage as St, TypeInner as Ti, VectorSize as Vs};
214
215        let ty_inner = &self.types[ty].inner;
216        match *binding {
217            crate::Binding::BuiltIn(built_in) => {
218                // Ignore the `invariant` field for the sake of duplicate checks,
219                // but use the original in error messages.
220                let canonical = if let crate::BuiltIn::Position { .. } = built_in {
221                    crate::BuiltIn::Position { invariant: false }
222                } else {
223                    built_in
224                };
225
226                if self.built_ins.contains(&canonical) {
227                    return Err(VaryingError::DuplicateBuiltIn(built_in));
228                }
229                self.built_ins.insert(canonical);
230
231                let required = match built_in {
232                    Bi::ClipDistance => Capabilities::CLIP_DISTANCE,
233                    Bi::CullDistance => Capabilities::CULL_DISTANCE,
234                    Bi::PrimitiveIndex => Capabilities::PRIMITIVE_INDEX,
235                    Bi::Barycentric => Capabilities::SHADER_BARYCENTRICS,
236                    Bi::ViewIndex => Capabilities::MULTIVIEW,
237                    Bi::SampleIndex => Capabilities::MULTISAMPLED_SHADING,
238                    Bi::NumSubgroups
239                    | Bi::SubgroupId
240                    | Bi::SubgroupSize
241                    | Bi::SubgroupInvocationId => Capabilities::SUBGROUP,
242                    _ => Capabilities::empty(),
243                };
244                if !self.capabilities.contains(required) {
245                    return Err(VaryingError::UnsupportedCapability(required));
246                }
247
248                if matches!(
249                    built_in,
250                    crate::BuiltIn::SubgroupId | crate::BuiltIn::SubgroupInvocationId
251                ) && ep.workgroup_size[1..].iter().any(|&s| s > 1)
252                {
253                    return Err(VaryingError::InvalidMultiDimensionalSubgroupBuiltIn);
254                }
255
256                let (visible, type_good) = match built_in {
257                    Bi::BaseInstance | Bi::BaseVertex | Bi::InstanceIndex | Bi::VertexIndex => (
258                        self.stage == St::Vertex && !self.output,
259                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
260                    ),
261                    Bi::DrawID => (
262                        // Always allowed in task/vertex stage. Allowed in mesh stage if there is no task stage in the pipeline.
263                        (self.stage == St::Vertex
264                            || self.stage == St::Task
265                            || (self.stage == St::Mesh && !self.has_task_payload))
266                            && !self.output,
267                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
268                    ),
269                    Bi::ClipDistance | Bi::CullDistance => (
270                        (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
271                        match *ty_inner {
272                            Ti::Array { base, size, .. } => {
273                                self.types[base].inner == Ti::Scalar(crate::Scalar::F32)
274                                    && match size {
275                                        crate::ArraySize::Constant(non_zero) => non_zero.get() <= 8,
276                                        _ => false,
277                                    }
278                            }
279                            _ => false,
280                        },
281                    ),
282                    Bi::PointSize => (
283                        (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
284                        *ty_inner == Ti::Scalar(crate::Scalar::F32),
285                    ),
286                    Bi::PointCoord => (
287                        self.stage == St::Fragment && !self.output,
288                        *ty_inner
289                            == Ti::Vector {
290                                size: Vs::Bi,
291                                scalar: crate::Scalar::F32,
292                            },
293                    ),
294                    Bi::Position { .. } => (
295                        match self.stage {
296                            St::Vertex | St::Mesh => self.output,
297                            St::Fragment => !self.output,
298                            St::Compute | St::Task => false,
299                        },
300                        *ty_inner
301                            == Ti::Vector {
302                                size: Vs::Quad,
303                                scalar: crate::Scalar::F32,
304                            },
305                    ),
306                    Bi::ViewIndex => (
307                        match self.stage {
308                            St::Vertex | St::Fragment | St::Task | St::Mesh => !self.output,
309                            St::Compute => false,
310                        },
311                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
312                    ),
313                    Bi::FragDepth => (
314                        self.stage == St::Fragment && self.output,
315                        *ty_inner == Ti::Scalar(crate::Scalar::F32),
316                    ),
317                    Bi::FrontFacing => (
318                        self.stage == St::Fragment && !self.output,
319                        *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
320                    ),
321                    Bi::PrimitiveIndex => (
322                        (self.stage == St::Fragment && !self.output)
323                            || (self.stage == St::Mesh
324                                && self.output
325                                && self.mesh_output_type == MeshOutputType::PrimitiveOutput),
326                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
327                    ),
328                    Bi::Barycentric => (
329                        self.stage == St::Fragment && !self.output,
330                        *ty_inner
331                            == Ti::Vector {
332                                size: Vs::Tri,
333                                scalar: crate::Scalar::F32,
334                            },
335                    ),
336                    Bi::SampleIndex => (
337                        self.stage == St::Fragment && !self.output,
338                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
339                    ),
340                    Bi::SampleMask => (
341                        self.stage == St::Fragment,
342                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
343                    ),
344                    Bi::LocalInvocationIndex => (
345                        self.stage.compute_like() && !self.output,
346                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
347                    ),
348                    Bi::GlobalInvocationId
349                    | Bi::LocalInvocationId
350                    | Bi::WorkGroupId
351                    | Bi::WorkGroupSize
352                    | Bi::NumWorkGroups => (
353                        self.stage.compute_like() && !self.output,
354                        *ty_inner
355                            == Ti::Vector {
356                                size: Vs::Tri,
357                                scalar: crate::Scalar::U32,
358                            },
359                    ),
360                    Bi::NumSubgroups | Bi::SubgroupId => (
361                        self.stage.compute_like() && !self.output,
362                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
363                    ),
364                    Bi::SubgroupSize | Bi::SubgroupInvocationId => (
365                        match self.stage {
366                            St::Compute | St::Fragment | St::Task | St::Mesh => !self.output,
367                            St::Vertex => false,
368                        },
369                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
370                    ),
371                    Bi::CullPrimitive => (
372                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
373                        *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
374                    ),
375                    Bi::PointIndex => (
376                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
377                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
378                    ),
379                    Bi::LineIndices => (
380                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
381                        *ty_inner
382                            == Ti::Vector {
383                                size: Vs::Bi,
384                                scalar: crate::Scalar::U32,
385                            },
386                    ),
387                    Bi::TriangleIndices => (
388                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
389                        *ty_inner
390                            == Ti::Vector {
391                                size: Vs::Tri,
392                                scalar: crate::Scalar::U32,
393                            },
394                    ),
395                    Bi::MeshTaskSize => (
396                        self.stage == St::Task && self.output,
397                        *ty_inner
398                            == Ti::Vector {
399                                size: Vs::Tri,
400                                scalar: crate::Scalar::U32,
401                            },
402                    ),
403                    // Validated elsewhere, shouldn't be here
404                    Bi::VertexCount | Bi::PrimitiveCount | Bi::Vertices | Bi::Primitives => {
405                        (false, true)
406                    }
407                };
408                match built_in {
409                    Bi::CullPrimitive
410                    | Bi::PointIndex
411                    | Bi::LineIndices
412                    | Bi::TriangleIndices
413                    | Bi::MeshTaskSize
414                    | Bi::VertexCount
415                    | Bi::PrimitiveCount
416                    | Bi::Vertices
417                    | Bi::Primitives => {
418                        if !self.capabilities.contains(Capabilities::MESH_SHADER) {
419                            return Err(VaryingError::UnsupportedCapability(
420                                Capabilities::MESH_SHADER,
421                            ));
422                        }
423                    }
424                    _ => (),
425                }
426
427                if !visible {
428                    return Err(VaryingError::InvalidBuiltInStage(built_in));
429                }
430                if !type_good {
431                    return Err(VaryingError::InvalidBuiltInType(built_in, ty_inner.clone()));
432                }
433            }
434            crate::Binding::Location {
435                location,
436                interpolation,
437                sampling,
438                blend_src,
439                per_primitive,
440            } => {
441                if per_primitive && !self.capabilities.contains(Capabilities::MESH_SHADER) {
442                    return Err(VaryingError::UnsupportedCapability(
443                        Capabilities::MESH_SHADER,
444                    ));
445                }
446                // Only IO-shareable types may be stored in locations.
447                if !self.type_info[ty.index()]
448                    .flags
449                    .contains(super::TypeFlags::IO_SHAREABLE)
450                {
451                    return Err(VaryingError::NotIOShareableType(ty));
452                }
453
454                // Check whether `per_primitive` is appropriate for this stage and direction.
455                if self.mesh_output_type == MeshOutputType::PrimitiveOutput {
456                    // All mesh shader `Location` outputs must be `per_primitive`.
457                    if !per_primitive {
458                        return Err(VaryingError::MissingPerPrimitive);
459                    }
460                } else if self.stage == crate::ShaderStage::Fragment && !self.output {
461                    // Fragment stage inputs may be `per_primitive`. We'll only
462                    // know if these are correct when the whole mesh pipeline is
463                    // created and we're paired with a specific mesh or vertex
464                    // shader.
465                } else if per_primitive {
466                    // All other `Location` bindings must not be `per_primitive`.
467                    return Err(VaryingError::InvalidPerPrimitive);
468                }
469
470                if let Some(blend_src) = blend_src {
471                    // `blend_src` is only valid if dual source blending was explicitly enabled,
472                    // see https://www.w3.org/TR/WGSL/#extension-dual_source_blending
473                    if !self
474                        .capabilities
475                        .contains(Capabilities::DUAL_SOURCE_BLENDING)
476                    {
477                        return Err(VaryingError::UnsupportedCapability(
478                            Capabilities::DUAL_SOURCE_BLENDING,
479                        ));
480                    }
481                    if self.stage != crate::ShaderStage::Fragment {
482                        return Err(VaryingError::InvalidAttributeInStage(
483                            "blend_src",
484                            self.stage,
485                        ));
486                    }
487                    if !self.output {
488                        return Err(VaryingError::InvalidInputAttributeInStage(
489                            "blend_src",
490                            self.stage,
491                        ));
492                    }
493                    if (blend_src != 0 && blend_src != 1) || location != 0 {
494                        return Err(VaryingError::InvalidBlendSrcIndex {
495                            location,
496                            blend_src,
497                        });
498                    }
499                    if !self.blend_src_mask.insert(blend_src as usize) {
500                        return Err(VaryingError::BindingCollisionBlendSrc { blend_src });
501                    }
502                } else if !self.location_mask.insert(location as usize)
503                    && self.flags.contains(super::ValidationFlags::BINDINGS)
504                {
505                    return Err(VaryingError::BindingCollision { location });
506                }
507
508                if let Some(interpolation) = interpolation {
509                    let invalid_sampling = match (interpolation, sampling) {
510                        (_, None)
511                        | (
512                            crate::Interpolation::Perspective | crate::Interpolation::Linear,
513                            Some(
514                                crate::Sampling::Center
515                                | crate::Sampling::Centroid
516                                | crate::Sampling::Sample,
517                            ),
518                        )
519                        | (
520                            crate::Interpolation::Flat,
521                            Some(crate::Sampling::First | crate::Sampling::Either),
522                        ) => None,
523                        (_, Some(invalid_sampling)) => Some(invalid_sampling),
524                    };
525                    if let Some(sampling) = invalid_sampling {
526                        return Err(VaryingError::InvalidInterpolationSamplingCombination {
527                            interpolation,
528                            sampling,
529                        });
530                    }
531                }
532
533                let needs_interpolation = match self.stage {
534                    crate::ShaderStage::Vertex => self.output,
535                    crate::ShaderStage::Fragment => !self.output && !per_primitive,
536                    crate::ShaderStage::Compute | crate::ShaderStage::Task => false,
537                    crate::ShaderStage::Mesh => self.output,
538                };
539
540                // It doesn't make sense to specify a sampling when `interpolation` is `Flat`, but
541                // SPIR-V and GLSL both explicitly tolerate such combinations of decorators /
542                // qualifiers, so we won't complain about that here.
543                let _ = sampling;
544
545                let required = match sampling {
546                    Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
547                    _ => Capabilities::empty(),
548                };
549                if !self.capabilities.contains(required) {
550                    return Err(VaryingError::UnsupportedCapability(required));
551                }
552
553                match ty_inner.scalar_kind() {
554                    Some(crate::ScalarKind::Float) => {
555                        if needs_interpolation && interpolation.is_none() {
556                            return Err(VaryingError::MissingInterpolation);
557                        }
558                    }
559                    Some(_) => {
560                        if needs_interpolation && interpolation != Some(crate::Interpolation::Flat)
561                        {
562                            return Err(VaryingError::InvalidInterpolation);
563                        }
564                    }
565                    None => return Err(VaryingError::InvalidType(ty)),
566                }
567            }
568        }
569
570        Ok(())
571    }
572
573    fn validate(
574        &mut self,
575        ep: &crate::EntryPoint,
576        ty: Handle<crate::Type>,
577        binding: Option<&crate::Binding>,
578    ) -> Result<(), WithSpan<VaryingError>> {
579        let span_context = self.types.get_span_context(ty);
580        match binding {
581            Some(binding) => self
582                .validate_impl(ep, ty, binding)
583                .map_err(|e| e.with_span_context(span_context)),
584            None => {
585                let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {
586                    if self.flags.contains(super::ValidationFlags::BINDINGS) {
587                        return Err(VaryingError::MissingBinding.with_span());
588                    } else {
589                        return Ok(());
590                    }
591                };
592
593                for (index, member) in members.iter().enumerate() {
594                    let span_context = self.types.get_span_context(ty);
595                    match member.binding {
596                        None => {
597                            if self.flags.contains(super::ValidationFlags::BINDINGS) {
598                                return Err(VaryingError::MemberMissingBinding(index as u32)
599                                    .with_span_context(span_context));
600                            }
601                        }
602                        Some(ref binding) => self
603                            .validate_impl(ep, member.ty, binding)
604                            .map_err(|e| e.with_span_context(span_context))?,
605                    }
606                }
607
608                if !self.blend_src_mask.is_empty() {
609                    let span_context = self.types.get_span_context(ty);
610
611                    // If there's any blend_src usage, it must apply to all members of which there must be exactly 2.
612                    if members.len() != 2 || self.blend_src_mask.len() != 2 {
613                        return Err(
614                            VaryingError::IncompleteBlendSrcUsage.with_span_context(span_context)
615                        );
616                    }
617                    // Also, all members must have the same type.
618                    if members[0].ty != members[1].ty {
619                        return Err(VaryingError::BlendSrcOutputTypeMismatch {
620                            blend_src_0_type: members[0].ty,
621                            blend_src_1_type: members[1].ty,
622                        }
623                        .with_span_context(span_context));
624                    }
625                }
626                Ok(())
627            }
628        }
629    }
630}
631
632impl super::Validator {
633    pub(super) fn validate_global_var(
634        &self,
635        var: &crate::GlobalVariable,
636        gctx: crate::proc::GlobalCtx,
637        mod_info: &ModuleInfo,
638        global_expr_kind: &crate::proc::ExpressionKindTracker,
639    ) -> Result<(), GlobalVariableError> {
640        use super::TypeFlags;
641
642        log::debug!("var {var:?}");
643        let inner_ty = match gctx.types[var.ty].inner {
644            // A binding array is (mostly) supposed to behave the same as a
645            // series of individually bound resources, so we can (mostly)
646            // validate a `binding_array<T>` as if it were just a plain `T`.
647            crate::TypeInner::BindingArray { base, .. } => match var.space {
648                crate::AddressSpace::Storage { .. } => {
649                    if !self
650                        .capabilities
651                        .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
652                    {
653                        return Err(GlobalVariableError::UnsupportedCapability(
654                            Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
655                        ));
656                    }
657                    base
658                }
659                crate::AddressSpace::Uniform => {
660                    if !self
661                        .capabilities
662                        .contains(Capabilities::BUFFER_BINDING_ARRAY)
663                    {
664                        return Err(GlobalVariableError::UnsupportedCapability(
665                            Capabilities::BUFFER_BINDING_ARRAY,
666                        ));
667                    }
668                    base
669                }
670                crate::AddressSpace::Handle => {
671                    match gctx.types[base].inner {
672                        crate::TypeInner::Image { class, .. } => match class {
673                            crate::ImageClass::Storage { .. } => {
674                                if !self
675                                    .capabilities
676                                    .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
677                                {
678                                    return Err(GlobalVariableError::UnsupportedCapability(
679                                        Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
680                                    ));
681                                }
682                            }
683                            crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {
684                                if !self
685                                    .capabilities
686                                    .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
687                                {
688                                    return Err(GlobalVariableError::UnsupportedCapability(
689                                        Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
690                                    ));
691                                }
692                            }
693                            crate::ImageClass::External => {
694                                // This should have been rejected in `validate_type`.
695                                unreachable!("binding arrays of external images are not supported");
696                            }
697                        },
698                        crate::TypeInner::Sampler { .. } => {
699                            if !self
700                                .capabilities
701                                .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
702                            {
703                                return Err(GlobalVariableError::UnsupportedCapability(
704                                    Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
705                                ));
706                            }
707                        }
708                        crate::TypeInner::AccelerationStructure { .. } => {
709                            return Err(GlobalVariableError::InvalidBindingArray(base));
710                        }
711                        crate::TypeInner::RayQuery { .. } => {
712                            // This should have been rejected in `validate_type`.
713                            unreachable!("binding arrays of ray queries are not supported");
714                        }
715                        _ => {
716                            // Fall through to the regular validation, which will reject `base`
717                            // as invalid in `AddressSpace::Handle`.
718                        }
719                    }
720                    base
721                }
722                _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
723            },
724            _ => var.ty,
725        };
726        let type_info = &self.types[inner_ty.index()];
727
728        let (required_type_flags, is_resource) = match var.space {
729            crate::AddressSpace::Function => {
730                return Err(GlobalVariableError::InvalidUsage(var.space))
731            }
732            crate::AddressSpace::Storage { access } => {
733                if let Err((ty_handle, disalignment)) = type_info.storage_layout {
734                    if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
735                        return Err(GlobalVariableError::Alignment(
736                            var.space,
737                            ty_handle,
738                            disalignment,
739                        ));
740                    }
741                }
742                if access == crate::StorageAccess::STORE {
743                    return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
744                }
745                (
746                    TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,
747                    true,
748                )
749            }
750            crate::AddressSpace::Uniform => {
751                if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
752                    if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
753                        return Err(GlobalVariableError::Alignment(
754                            var.space,
755                            ty_handle,
756                            disalignment,
757                        ));
758                    }
759                }
760                (
761                    TypeFlags::DATA
762                        | TypeFlags::COPY
763                        | TypeFlags::SIZED
764                        | TypeFlags::HOST_SHAREABLE
765                        | TypeFlags::CREATION_RESOLVED,
766                    true,
767                )
768            }
769            crate::AddressSpace::Handle => {
770                match gctx.types[inner_ty].inner {
771                    crate::TypeInner::Image { class, .. } => match class {
772                        crate::ImageClass::Storage {
773                            format:
774                                crate::StorageFormat::R16Unorm
775                                | crate::StorageFormat::R16Snorm
776                                | crate::StorageFormat::Rg16Unorm
777                                | crate::StorageFormat::Rg16Snorm
778                                | crate::StorageFormat::Rgba16Unorm
779                                | crate::StorageFormat::Rgba16Snorm,
780                            ..
781                        } => {
782                            if !self
783                                .capabilities
784                                .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)
785                            {
786                                return Err(GlobalVariableError::UnsupportedCapability(
787                                    Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
788                                ));
789                            }
790                        }
791                        _ => {}
792                    },
793                    crate::TypeInner::Sampler { .. }
794                    | crate::TypeInner::AccelerationStructure { .. }
795                    | crate::TypeInner::RayQuery { .. } => {}
796                    _ => {
797                        return Err(GlobalVariableError::InvalidType(var.space));
798                    }
799                }
800
801                (TypeFlags::empty(), true)
802            }
803            crate::AddressSpace::Private => (
804                TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,
805                false,
806            ),
807            crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
808            crate::AddressSpace::TaskPayload => {
809                if !self.capabilities.contains(Capabilities::MESH_SHADER) {
810                    return Err(GlobalVariableError::UnsupportedCapability(
811                        Capabilities::MESH_SHADER,
812                    ));
813                }
814                (TypeFlags::DATA | TypeFlags::SIZED, false)
815            }
816            crate::AddressSpace::Immediate => {
817                if !self.capabilities.contains(Capabilities::IMMEDIATES) {
818                    return Err(GlobalVariableError::UnsupportedCapability(
819                        Capabilities::IMMEDIATES,
820                    ));
821                }
822                if let Err(ref err) = type_info.immediates_compatibility {
823                    return Err(GlobalVariableError::InvalidImmediateType(err.clone()));
824                }
825                (
826                    TypeFlags::DATA
827                        | TypeFlags::COPY
828                        | TypeFlags::HOST_SHAREABLE
829                        | TypeFlags::SIZED,
830                    false,
831                )
832            }
833        };
834
835        if !type_info.flags.contains(required_type_flags) {
836            return Err(GlobalVariableError::MissingTypeFlags {
837                seen: type_info.flags,
838                required: required_type_flags,
839            });
840        }
841
842        if is_resource != var.binding.is_some() {
843            if self.flags.contains(super::ValidationFlags::BINDINGS) {
844                return Err(GlobalVariableError::InvalidBinding);
845            }
846        }
847
848        if var.space == crate::AddressSpace::TaskPayload {
849            let ty = &gctx.types[var.ty].inner;
850            // HLSL doesn't allow zero sized payloads.
851            if ty.try_size(gctx) == Some(0) {
852                return Err(GlobalVariableError::ZeroSizedTaskPayload);
853            }
854        }
855
856        if let Some(init) = var.init {
857            match var.space {
858                crate::AddressSpace::Private | crate::AddressSpace::Function => {}
859                _ => {
860                    return Err(GlobalVariableError::InitializerNotAllowed(var.space));
861                }
862            }
863
864            if !global_expr_kind.is_const_or_override(init) {
865                return Err(GlobalVariableError::InitializerExprType);
866            }
867
868            if !gctx.compare_types(
869                &crate::proc::TypeResolution::Handle(var.ty),
870                &mod_info[init],
871            ) {
872                return Err(GlobalVariableError::InitializerType);
873            }
874        }
875
876        Ok(())
877    }
878
879    /// Validate the mesh shader output type `ty`, used as `mesh_output_type`.
880    fn validate_mesh_output_type(
881        &mut self,
882        ep: &crate::EntryPoint,
883        module: &crate::Module,
884        ty: Handle<crate::Type>,
885        mesh_output_type: MeshOutputType,
886    ) -> Result<(), WithSpan<EntryPointError>> {
887        if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {
888            return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));
889        }
890        let mut result_built_ins = crate::FastHashSet::default();
891        let mut ctx = VaryingContext {
892            stage: ep.stage,
893            output: true,
894            types: &module.types,
895            type_info: &self.types,
896            location_mask: &mut self.location_mask,
897            blend_src_mask: &mut self.blend_src_mask,
898            built_ins: &mut result_built_ins,
899            capabilities: self.capabilities,
900            flags: self.flags,
901            mesh_output_type,
902            has_task_payload: ep.task_payload.is_some(),
903        };
904        ctx.validate(ep, ty, None)
905            .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
906        if mesh_output_type == MeshOutputType::PrimitiveOutput {
907            let mut num_indices_builtins = 0;
908            if result_built_ins.contains(&crate::BuiltIn::PointIndex) {
909                num_indices_builtins += 1;
910            }
911            if result_built_ins.contains(&crate::BuiltIn::LineIndices) {
912                num_indices_builtins += 1;
913            }
914            if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {
915                num_indices_builtins += 1;
916            }
917            if num_indices_builtins != 1 {
918                return Err(EntryPointError::InvalidMeshPrimitiveOutputType
919                    .with_span_handle(ty, &module.types));
920            }
921        } else if mesh_output_type == MeshOutputType::VertexOutput
922            && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
923        {
924            return Err(
925                EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)
926            );
927        }
928
929        Ok(())
930    }
931
932    pub(super) fn validate_entry_point(
933        &mut self,
934        ep: &crate::EntryPoint,
935        module: &crate::Module,
936        mod_info: &ModuleInfo,
937    ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
938        if matches!(
939            ep.stage,
940            crate::ShaderStage::Task | crate::ShaderStage::Mesh
941        ) && !self.capabilities.contains(Capabilities::MESH_SHADER)
942        {
943            return Err(
944                EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),
945            );
946        }
947        if ep.early_depth_test.is_some() {
948            let required = Capabilities::EARLY_DEPTH_TEST;
949            if !self.capabilities.contains(required) {
950                return Err(
951                    EntryPointError::Result(VaryingError::UnsupportedCapability(required))
952                        .with_span(),
953                );
954            }
955
956            if ep.stage != crate::ShaderStage::Fragment {
957                return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
958            }
959        }
960
961        if ep.stage.compute_like() {
962            if ep
963                .workgroup_size
964                .iter()
965                .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
966            {
967                return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
968            }
969        } else if ep.workgroup_size != [0; 3] {
970            return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
971        }
972
973        match (ep.stage, &ep.mesh_info) {
974            (crate::ShaderStage::Mesh, &None) => {
975                return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());
976            }
977            (crate::ShaderStage::Mesh, &Some(..)) => {}
978            (_, &Some(_)) => {
979                return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());
980            }
981            (_, _) => {}
982        }
983
984        let mut info = self
985            .validate_function(&ep.function, module, mod_info, true)
986            .map_err(WithSpan::into_other)?;
987
988        // Validate the task shader payload.
989        match ep.stage {
990            // Task shaders must produce a payload.
991            crate::ShaderStage::Task => {
992                let Some(handle) = ep.task_payload else {
993                    return Err(EntryPointError::ExpectedTaskPayload.with_span());
994                };
995                if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
996                    return Err(EntryPointError::TaskPayloadWrongAddressSpace
997                        .with_span_handle(handle, &module.global_variables));
998                }
999                info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);
1000            }
1001
1002            // Mesh shaders may accept a payload.
1003            crate::ShaderStage::Mesh => {
1004                if let Some(handle) = ep.task_payload {
1005                    if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1006                        return Err(EntryPointError::TaskPayloadWrongAddressSpace
1007                            .with_span_handle(handle, &module.global_variables));
1008                    }
1009                    info.insert_global_use(GlobalUse::READ, handle);
1010                }
1011                if let Some(ref mesh_info) = ep.mesh_info {
1012                    info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);
1013                }
1014            }
1015
1016            // Other stages must not have a payload.
1017            _ => {
1018                if let Some(handle) = ep.task_payload {
1019                    return Err(EntryPointError::UnexpectedTaskPayload
1020                        .with_span_handle(handle, &module.global_variables));
1021                }
1022            }
1023        }
1024
1025        {
1026            use super::ShaderStages;
1027
1028            let stage_bit = match ep.stage {
1029                crate::ShaderStage::Vertex => ShaderStages::VERTEX,
1030                crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
1031                crate::ShaderStage::Compute => ShaderStages::COMPUTE,
1032                crate::ShaderStage::Mesh => ShaderStages::MESH,
1033                crate::ShaderStage::Task => ShaderStages::TASK,
1034            };
1035
1036            if !info.available_stages.contains(stage_bit) {
1037                return Err(EntryPointError::ForbiddenStageOperations.with_span());
1038            }
1039        }
1040
1041        self.location_mask.clear();
1042        let mut argument_built_ins = crate::FastHashSet::default();
1043        // TODO: add span info to function arguments
1044        for (index, fa) in ep.function.arguments.iter().enumerate() {
1045            let mut ctx = VaryingContext {
1046                stage: ep.stage,
1047                output: false,
1048                types: &module.types,
1049                type_info: &self.types,
1050                location_mask: &mut self.location_mask,
1051                blend_src_mask: &mut self.blend_src_mask,
1052                built_ins: &mut argument_built_ins,
1053                capabilities: self.capabilities,
1054                flags: self.flags,
1055                mesh_output_type: MeshOutputType::None,
1056                has_task_payload: ep.task_payload.is_some(),
1057            };
1058            ctx.validate(ep, fa.ty, fa.binding.as_ref())
1059                .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
1060        }
1061
1062        self.location_mask.clear();
1063        if let Some(ref fr) = ep.function.result {
1064            let mut result_built_ins = crate::FastHashSet::default();
1065            let mut ctx = VaryingContext {
1066                stage: ep.stage,
1067                output: true,
1068                types: &module.types,
1069                type_info: &self.types,
1070                location_mask: &mut self.location_mask,
1071                blend_src_mask: &mut self.blend_src_mask,
1072                built_ins: &mut result_built_ins,
1073                capabilities: self.capabilities,
1074                flags: self.flags,
1075                mesh_output_type: MeshOutputType::None,
1076                has_task_payload: ep.task_payload.is_some(),
1077            };
1078            ctx.validate(ep, fr.ty, fr.binding.as_ref())
1079                .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1080            if ep.stage == crate::ShaderStage::Vertex
1081                && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1082            {
1083                return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1084            }
1085            if ep.stage == crate::ShaderStage::Mesh {
1086                return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span());
1087            }
1088            // Task shaders must have a single `MeshTaskSize` output, and nothing else.
1089            if ep.stage == crate::ShaderStage::Task {
1090                let ok = result_built_ins.contains(&crate::BuiltIn::MeshTaskSize)
1091                    && result_built_ins.len() == 1
1092                    && self.location_mask.is_empty();
1093                if !ok {
1094                    return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1095                }
1096            }
1097            if !self.blend_src_mask.is_empty() {
1098                info.dual_source_blending = true;
1099            }
1100        } else if ep.stage == crate::ShaderStage::Vertex {
1101            return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1102        } else if ep.stage == crate::ShaderStage::Task {
1103            return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1104        }
1105
1106        {
1107            let mut used_immediates = module
1108                .global_variables
1109                .iter()
1110                .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
1111                .map(|(handle, _)| handle)
1112                .filter(|&handle| !info[handle].is_empty());
1113            // Check if there is more than one immediate data, and error if so.
1114            // Use a loop for when returning multiple errors is supported.
1115            if let Some(handle) = used_immediates.nth(1) {
1116                return Err(EntryPointError::MoreThanOneImmediateUsed
1117                    .with_span_handle(handle, &module.global_variables));
1118            }
1119        }
1120
1121        self.ep_resource_bindings.clear();
1122        for (var_handle, var) in module.global_variables.iter() {
1123            let usage = info[var_handle];
1124            if usage.is_empty() {
1125                continue;
1126            }
1127
1128            if var.space == crate::AddressSpace::TaskPayload {
1129                if ep.task_payload != Some(var_handle) {
1130                    return Err(EntryPointError::WrongTaskPayloadUsed
1131                        .with_span_handle(var_handle, &module.global_variables));
1132                }
1133                let size = module.types[var.ty].inner.size(module.to_ctx());
1134                if size < 4 {
1135                    return Err(EntryPointError::TaskPayloadTooSmall(size)
1136                        .with_span_handle(var_handle, &module.global_variables));
1137                }
1138            }
1139
1140            let allowed_usage = match var.space {
1141                crate::AddressSpace::Function => unreachable!(),
1142                crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
1143                crate::AddressSpace::Storage { access } => storage_usage(access),
1144                crate::AddressSpace::Handle => match module.types[var.ty].inner {
1145                    crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
1146                        crate::TypeInner::Image {
1147                            class: crate::ImageClass::Storage { access, .. },
1148                            ..
1149                        } => storage_usage(access),
1150                        _ => GlobalUse::READ | GlobalUse::QUERY,
1151                    },
1152                    crate::TypeInner::Image {
1153                        class: crate::ImageClass::Storage { access, .. },
1154                        ..
1155                    } => storage_usage(access),
1156                    _ => GlobalUse::READ | GlobalUse::QUERY,
1157                },
1158                crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {
1159                    GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY
1160                }
1161                crate::AddressSpace::TaskPayload => {
1162                    GlobalUse::READ
1163                        | GlobalUse::QUERY
1164                        | if ep.stage == crate::ShaderStage::Task {
1165                            GlobalUse::WRITE
1166                        } else {
1167                            GlobalUse::empty()
1168                        }
1169                }
1170                crate::AddressSpace::Immediate => GlobalUse::READ,
1171            };
1172            if !allowed_usage.contains(usage) {
1173                log::warn!("\tUsage error for: {var:?}");
1174                log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}");
1175                return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
1176                    .with_span_handle(var_handle, &module.global_variables));
1177            }
1178
1179            if let Some(ref bind) = var.binding {
1180                if !self.ep_resource_bindings.insert(*bind) {
1181                    if self.flags.contains(super::ValidationFlags::BINDINGS) {
1182                        return Err(EntryPointError::BindingCollision(var_handle)
1183                            .with_span_handle(var_handle, &module.global_variables));
1184                    }
1185                }
1186            }
1187        }
1188
1189        // If this is a `Mesh` entry point, check its vertex and primitive output types.
1190        // We verified previously that only mesh shaders can have `mesh_info`.
1191        if let &Some(ref mesh_info) = &ep.mesh_info {
1192            if module.global_variables[mesh_info.output_variable].space
1193                != crate::AddressSpace::WorkGroup
1194            {
1195                return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
1196            }
1197
1198            let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);
1199            if let Some(e) = implied.2 {
1200                return Err(e);
1201            }
1202
1203            if let Some(e) = mesh_info.max_vertices_override {
1204                if let crate::Expression::Override(o) = module.global_expressions[e] {
1205                    if implied.1[0] != Some(o) {
1206                        return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1207                    }
1208                }
1209            }
1210            if let Some(e) = mesh_info.max_primitives_override {
1211                if let crate::Expression::Override(o) = module.global_expressions[e] {
1212                    if implied.1[1] != Some(o) {
1213                        return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1214                    }
1215                }
1216            }
1217
1218            implied.0.max_vertices_override = mesh_info.max_vertices_override;
1219            implied.0.max_primitives_override = mesh_info.max_primitives_override;
1220            if implied.0 != *mesh_info {
1221                return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1222            }
1223            if mesh_info.topology == crate::MeshOutputTopology::Points
1224                && !self
1225                    .capabilities
1226                    .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)
1227            {
1228                return Err(EntryPointError::UnsupportedCapability(
1229                    Capabilities::MESH_SHADER_POINT_TOPOLOGY,
1230                )
1231                .with_span());
1232            }
1233
1234            self.validate_mesh_output_type(
1235                ep,
1236                module,
1237                mesh_info.vertex_output_type,
1238                MeshOutputType::VertexOutput,
1239            )?;
1240            self.validate_mesh_output_type(
1241                ep,
1242                module,
1243                mesh_info.primitive_output_type,
1244                MeshOutputType::PrimitiveOutput,
1245            )?;
1246        }
1247
1248        Ok(info)
1249    }
1250}