Coverage Report

Created: 2025-12-11 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wgpu/naga/src/valid/interface.rs
Line
Count
Source
1
use alloc::vec::Vec;
2
3
use bit_set::BitSet;
4
5
use super::{
6
    analyzer::{FunctionInfo, GlobalUse},
7
    Capabilities, Disalignment, FunctionError, ImmediateError, ModuleInfo,
8
};
9
use crate::arena::{Handle, UniqueArena};
10
use crate::span::{AddSpan as _, MapErrWithSpan as _, SpanProvider as _, WithSpan};
11
12
const MAX_WORKGROUP_SIZE: u32 = 0x4000;
13
14
#[derive(Clone, Debug, thiserror::Error)]
15
#[cfg_attr(test, derive(PartialEq))]
16
pub 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))]
54
pub 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))]
107
pub 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
171
0
fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
172
0
    let mut storage_usage = GlobalUse::QUERY;
173
0
    if access.contains(crate::StorageAccess::LOAD) {
174
0
        storage_usage |= GlobalUse::READ;
175
0
    }
176
0
    if access.contains(crate::StorageAccess::STORE) {
177
0
        storage_usage |= GlobalUse::WRITE;
178
0
    }
179
0
    if access.contains(crate::StorageAccess::ATOMIC) {
180
0
        storage_usage |= GlobalUse::ATOMIC;
181
0
    }
182
0
    storage_usage
183
0
}
184
185
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
186
enum MeshOutputType {
187
    None,
188
    VertexOutput,
189
    PrimitiveOutput,
190
}
191
192
struct 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
206
impl VaryingContext<'_> {
207
0
    fn validate_impl(
208
0
        &mut self,
209
0
        ep: &crate::EntryPoint,
210
0
        ty: Handle<crate::Type>,
211
0
        binding: &crate::Binding,
212
0
    ) -> Result<(), VaryingError> {
213
        use crate::{BuiltIn as Bi, ShaderStage as St, TypeInner as Ti, VectorSize as Vs};
214
215
0
        let ty_inner = &self.types[ty].inner;
216
0
        match *binding {
217
0
            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
0
                let canonical = if let crate::BuiltIn::Position { .. } = built_in {
221
0
                    crate::BuiltIn::Position { invariant: false }
222
                } else {
223
0
                    built_in
224
                };
225
226
0
                if self.built_ins.contains(&canonical) {
227
0
                    return Err(VaryingError::DuplicateBuiltIn(built_in));
228
0
                }
229
0
                self.built_ins.insert(canonical);
230
231
0
                let required = match built_in {
232
0
                    Bi::ClipDistance => Capabilities::CLIP_DISTANCE,
233
0
                    Bi::CullDistance => Capabilities::CULL_DISTANCE,
234
0
                    Bi::PrimitiveIndex => Capabilities::PRIMITIVE_INDEX,
235
0
                    Bi::Barycentric => Capabilities::SHADER_BARYCENTRICS,
236
0
                    Bi::ViewIndex => Capabilities::MULTIVIEW,
237
0
                    Bi::SampleIndex => Capabilities::MULTISAMPLED_SHADING,
238
                    Bi::NumSubgroups
239
                    | Bi::SubgroupId
240
                    | Bi::SubgroupSize
241
0
                    | Bi::SubgroupInvocationId => Capabilities::SUBGROUP,
242
0
                    _ => Capabilities::empty(),
243
                };
244
0
                if !self.capabilities.contains(required) {
245
0
                    return Err(VaryingError::UnsupportedCapability(required));
246
0
                }
247
248
0
                if matches!(
249
0
                    built_in,
250
                    crate::BuiltIn::SubgroupId | crate::BuiltIn::SubgroupInvocationId
251
0
                ) && ep.workgroup_size[1..].iter().any(|&s| s > 1)
252
                {
253
0
                    return Err(VaryingError::InvalidMultiDimensionalSubgroupBuiltIn);
254
0
                }
255
256
0
                let (visible, type_good) = match built_in {
257
                    Bi::BaseInstance | Bi::BaseVertex | Bi::InstanceIndex | Bi::VertexIndex => (
258
0
                        self.stage == St::Vertex && !self.output,
259
0
                        *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
0
                        (self.stage == St::Vertex
264
0
                            || self.stage == St::Task
265
0
                            || (self.stage == St::Mesh && !self.has_task_payload))
266
0
                            && !self.output,
267
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
268
                    ),
269
                    Bi::ClipDistance | Bi::CullDistance => (
270
0
                        (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
271
0
                        match *ty_inner {
272
0
                            Ti::Array { base, size, .. } => {
273
0
                                self.types[base].inner == Ti::Scalar(crate::Scalar::F32)
274
0
                                    && match size {
275
0
                                        crate::ArraySize::Constant(non_zero) => non_zero.get() <= 8,
276
0
                                        _ => false,
277
                                    }
278
                            }
279
0
                            _ => false,
280
                        },
281
                    ),
282
                    Bi::PointSize => (
283
0
                        (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
284
0
                        *ty_inner == Ti::Scalar(crate::Scalar::F32),
285
                    ),
286
                    Bi::PointCoord => (
287
0
                        self.stage == St::Fragment && !self.output,
288
0
                        *ty_inner
289
0
                            == Ti::Vector {
290
0
                                size: Vs::Bi,
291
0
                                scalar: crate::Scalar::F32,
292
0
                            },
293
                    ),
294
                    Bi::Position { .. } => (
295
0
                        match self.stage {
296
0
                            St::Vertex | St::Mesh => self.output,
297
0
                            St::Fragment => !self.output,
298
0
                            St::Compute | St::Task => false,
299
                        },
300
0
                        *ty_inner
301
0
                            == Ti::Vector {
302
0
                                size: Vs::Quad,
303
0
                                scalar: crate::Scalar::F32,
304
0
                            },
305
                    ),
306
                    Bi::ViewIndex => (
307
0
                        match self.stage {
308
0
                            St::Vertex | St::Fragment | St::Task | St::Mesh => !self.output,
309
0
                            St::Compute => false,
310
                        },
311
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
312
                    ),
313
                    Bi::FragDepth => (
314
0
                        self.stage == St::Fragment && self.output,
315
0
                        *ty_inner == Ti::Scalar(crate::Scalar::F32),
316
                    ),
317
                    Bi::FrontFacing => (
318
0
                        self.stage == St::Fragment && !self.output,
319
0
                        *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
320
                    ),
321
                    Bi::PrimitiveIndex => (
322
0
                        (self.stage == St::Fragment && !self.output)
323
0
                            || (self.stage == St::Mesh
324
0
                                && self.output
325
0
                                && self.mesh_output_type == MeshOutputType::PrimitiveOutput),
326
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
327
                    ),
328
                    Bi::Barycentric => (
329
0
                        self.stage == St::Fragment && !self.output,
330
0
                        *ty_inner
331
0
                            == Ti::Vector {
332
0
                                size: Vs::Tri,
333
0
                                scalar: crate::Scalar::F32,
334
0
                            },
335
                    ),
336
                    Bi::SampleIndex => (
337
0
                        self.stage == St::Fragment && !self.output,
338
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
339
                    ),
340
0
                    Bi::SampleMask => (
341
0
                        self.stage == St::Fragment,
342
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
343
0
                    ),
344
                    Bi::LocalInvocationIndex => (
345
0
                        self.stage.compute_like() && !self.output,
346
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
347
                    ),
348
                    Bi::GlobalInvocationId
349
                    | Bi::LocalInvocationId
350
                    | Bi::WorkGroupId
351
                    | Bi::WorkGroupSize
352
                    | Bi::NumWorkGroups => (
353
0
                        self.stage.compute_like() && !self.output,
354
0
                        *ty_inner
355
0
                            == Ti::Vector {
356
0
                                size: Vs::Tri,
357
0
                                scalar: crate::Scalar::U32,
358
0
                            },
359
                    ),
360
                    Bi::NumSubgroups | Bi::SubgroupId => (
361
0
                        self.stage.compute_like() && !self.output,
362
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
363
                    ),
364
                    Bi::SubgroupSize | Bi::SubgroupInvocationId => (
365
0
                        match self.stage {
366
0
                            St::Compute | St::Fragment | St::Task | St::Mesh => !self.output,
367
0
                            St::Vertex => false,
368
                        },
369
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
370
                    ),
371
0
                    Bi::CullPrimitive => (
372
0
                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
373
0
                        *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
374
0
                    ),
375
0
                    Bi::PointIndex => (
376
0
                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
377
0
                        *ty_inner == Ti::Scalar(crate::Scalar::U32),
378
0
                    ),
379
0
                    Bi::LineIndices => (
380
0
                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
381
0
                        *ty_inner
382
0
                            == Ti::Vector {
383
0
                                size: Vs::Bi,
384
0
                                scalar: crate::Scalar::U32,
385
0
                            },
386
0
                    ),
387
0
                    Bi::TriangleIndices => (
388
0
                        self.mesh_output_type == MeshOutputType::PrimitiveOutput,
389
0
                        *ty_inner
390
0
                            == Ti::Vector {
391
0
                                size: Vs::Tri,
392
0
                                scalar: crate::Scalar::U32,
393
0
                            },
394
0
                    ),
395
                    Bi::MeshTaskSize => (
396
0
                        self.stage == St::Task && self.output,
397
0
                        *ty_inner
398
0
                            == Ti::Vector {
399
0
                                size: Vs::Tri,
400
0
                                scalar: crate::Scalar::U32,
401
0
                            },
402
                    ),
403
                    // Validated elsewhere, shouldn't be here
404
                    Bi::VertexCount | Bi::PrimitiveCount | Bi::Vertices | Bi::Primitives => {
405
0
                        (false, true)
406
                    }
407
                };
408
0
                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
0
                        if !self.capabilities.contains(Capabilities::MESH_SHADER) {
419
0
                            return Err(VaryingError::UnsupportedCapability(
420
0
                                Capabilities::MESH_SHADER,
421
0
                            ));
422
0
                        }
423
                    }
424
0
                    _ => (),
425
                }
426
427
0
                if !visible {
428
0
                    return Err(VaryingError::InvalidBuiltInStage(built_in));
429
0
                }
430
0
                if !type_good {
431
0
                    return Err(VaryingError::InvalidBuiltInType(built_in, ty_inner.clone()));
432
0
                }
433
            }
434
            crate::Binding::Location {
435
0
                location,
436
0
                interpolation,
437
0
                sampling,
438
0
                blend_src,
439
0
                per_primitive,
440
            } => {
441
0
                if per_primitive && !self.capabilities.contains(Capabilities::MESH_SHADER) {
442
0
                    return Err(VaryingError::UnsupportedCapability(
443
0
                        Capabilities::MESH_SHADER,
444
0
                    ));
445
0
                }
446
                // Only IO-shareable types may be stored in locations.
447
0
                if !self.type_info[ty.index()]
448
0
                    .flags
449
0
                    .contains(super::TypeFlags::IO_SHAREABLE)
450
                {
451
0
                    return Err(VaryingError::NotIOShareableType(ty));
452
0
                }
453
454
                // Check whether `per_primitive` is appropriate for this stage and direction.
455
0
                if self.mesh_output_type == MeshOutputType::PrimitiveOutput {
456
                    // All mesh shader `Location` outputs must be `per_primitive`.
457
0
                    if !per_primitive {
458
0
                        return Err(VaryingError::MissingPerPrimitive);
459
0
                    }
460
0
                } else if self.stage == crate::ShaderStage::Fragment && !self.output {
461
0
                    // Fragment stage inputs may be `per_primitive`. We'll only
462
0
                    // know if these are correct when the whole mesh pipeline is
463
0
                    // created and we're paired with a specific mesh or vertex
464
0
                    // shader.
465
0
                } else if per_primitive {
466
                    // All other `Location` bindings must not be `per_primitive`.
467
0
                    return Err(VaryingError::InvalidPerPrimitive);
468
0
                }
469
470
0
                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
0
                    if !self
474
0
                        .capabilities
475
0
                        .contains(Capabilities::DUAL_SOURCE_BLENDING)
476
                    {
477
0
                        return Err(VaryingError::UnsupportedCapability(
478
0
                            Capabilities::DUAL_SOURCE_BLENDING,
479
0
                        ));
480
0
                    }
481
0
                    if self.stage != crate::ShaderStage::Fragment {
482
0
                        return Err(VaryingError::InvalidAttributeInStage(
483
0
                            "blend_src",
484
0
                            self.stage,
485
0
                        ));
486
0
                    }
487
0
                    if !self.output {
488
0
                        return Err(VaryingError::InvalidInputAttributeInStage(
489
0
                            "blend_src",
490
0
                            self.stage,
491
0
                        ));
492
0
                    }
493
0
                    if (blend_src != 0 && blend_src != 1) || location != 0 {
494
0
                        return Err(VaryingError::InvalidBlendSrcIndex {
495
0
                            location,
496
0
                            blend_src,
497
0
                        });
498
0
                    }
499
0
                    if !self.blend_src_mask.insert(blend_src as usize) {
500
0
                        return Err(VaryingError::BindingCollisionBlendSrc { blend_src });
501
0
                    }
502
0
                } else if !self.location_mask.insert(location as usize)
503
0
                    && self.flags.contains(super::ValidationFlags::BINDINGS)
504
                {
505
0
                    return Err(VaryingError::BindingCollision { location });
506
0
                }
507
508
0
                if let Some(interpolation) = interpolation {
509
0
                    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
0
                        ) => None,
523
0
                        (_, Some(invalid_sampling)) => Some(invalid_sampling),
524
                    };
525
0
                    if let Some(sampling) = invalid_sampling {
526
0
                        return Err(VaryingError::InvalidInterpolationSamplingCombination {
527
0
                            interpolation,
528
0
                            sampling,
529
0
                        });
530
0
                    }
531
0
                }
532
533
0
                let needs_interpolation = match self.stage {
534
0
                    crate::ShaderStage::Vertex => self.output,
535
0
                    crate::ShaderStage::Fragment => !self.output && !per_primitive,
536
0
                    crate::ShaderStage::Compute | crate::ShaderStage::Task => false,
537
0
                    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
0
                let _ = sampling;
544
545
0
                let required = match sampling {
546
0
                    Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
547
0
                    _ => Capabilities::empty(),
548
                };
549
0
                if !self.capabilities.contains(required) {
550
0
                    return Err(VaryingError::UnsupportedCapability(required));
551
0
                }
552
553
0
                match ty_inner.scalar_kind() {
554
                    Some(crate::ScalarKind::Float) => {
555
0
                        if needs_interpolation && interpolation.is_none() {
556
0
                            return Err(VaryingError::MissingInterpolation);
557
0
                        }
558
                    }
559
                    Some(_) => {
560
0
                        if needs_interpolation && interpolation != Some(crate::Interpolation::Flat)
561
                        {
562
0
                            return Err(VaryingError::InvalidInterpolation);
563
0
                        }
564
                    }
565
0
                    None => return Err(VaryingError::InvalidType(ty)),
566
                }
567
            }
568
        }
569
570
0
        Ok(())
571
0
    }
572
573
0
    fn validate(
574
0
        &mut self,
575
0
        ep: &crate::EntryPoint,
576
0
        ty: Handle<crate::Type>,
577
0
        binding: Option<&crate::Binding>,
578
0
    ) -> Result<(), WithSpan<VaryingError>> {
579
0
        let span_context = self.types.get_span_context(ty);
580
0
        match binding {
581
0
            Some(binding) => self
582
0
                .validate_impl(ep, ty, binding)
583
0
                .map_err(|e| e.with_span_context(span_context)),
584
            None => {
585
0
                let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {
586
0
                    if self.flags.contains(super::ValidationFlags::BINDINGS) {
587
0
                        return Err(VaryingError::MissingBinding.with_span());
588
                    } else {
589
0
                        return Ok(());
590
                    }
591
                };
592
593
0
                for (index, member) in members.iter().enumerate() {
594
0
                    let span_context = self.types.get_span_context(ty);
595
0
                    match member.binding {
596
                        None => {
597
0
                            if self.flags.contains(super::ValidationFlags::BINDINGS) {
598
0
                                return Err(VaryingError::MemberMissingBinding(index as u32)
599
0
                                    .with_span_context(span_context));
600
0
                            }
601
                        }
602
0
                        Some(ref binding) => self
603
0
                            .validate_impl(ep, member.ty, binding)
604
0
                            .map_err(|e| e.with_span_context(span_context))?,
605
                    }
606
                }
607
608
0
                if !self.blend_src_mask.is_empty() {
609
0
                    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
0
                    if members.len() != 2 || self.blend_src_mask.len() != 2 {
613
0
                        return Err(
614
0
                            VaryingError::IncompleteBlendSrcUsage.with_span_context(span_context)
615
0
                        );
616
0
                    }
617
                    // Also, all members must have the same type.
618
0
                    if members[0].ty != members[1].ty {
619
0
                        return Err(VaryingError::BlendSrcOutputTypeMismatch {
620
0
                            blend_src_0_type: members[0].ty,
621
0
                            blend_src_1_type: members[1].ty,
622
0
                        }
623
0
                        .with_span_context(span_context));
624
0
                    }
625
0
                }
626
0
                Ok(())
627
            }
628
        }
629
0
    }
630
}
631
632
impl super::Validator {
633
0
    pub(super) fn validate_global_var(
634
0
        &self,
635
0
        var: &crate::GlobalVariable,
636
0
        gctx: crate::proc::GlobalCtx,
637
0
        mod_info: &ModuleInfo,
638
0
        global_expr_kind: &crate::proc::ExpressionKindTracker,
639
0
    ) -> Result<(), GlobalVariableError> {
640
        use super::TypeFlags;
641
642
0
        log::debug!("var {var:?}");
643
0
        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
0
            crate::TypeInner::BindingArray { base, .. } => match var.space {
648
                crate::AddressSpace::Storage { .. } => {
649
0
                    if !self
650
0
                        .capabilities
651
0
                        .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
652
                    {
653
0
                        return Err(GlobalVariableError::UnsupportedCapability(
654
0
                            Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
655
0
                        ));
656
0
                    }
657
0
                    base
658
                }
659
                crate::AddressSpace::Uniform => {
660
0
                    if !self
661
0
                        .capabilities
662
0
                        .contains(Capabilities::BUFFER_BINDING_ARRAY)
663
                    {
664
0
                        return Err(GlobalVariableError::UnsupportedCapability(
665
0
                            Capabilities::BUFFER_BINDING_ARRAY,
666
0
                        ));
667
0
                    }
668
0
                    base
669
                }
670
                crate::AddressSpace::Handle => {
671
0
                    match gctx.types[base].inner {
672
0
                        crate::TypeInner::Image { class, .. } => match class {
673
                            crate::ImageClass::Storage { .. } => {
674
0
                                if !self
675
0
                                    .capabilities
676
0
                                    .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
677
                                {
678
0
                                    return Err(GlobalVariableError::UnsupportedCapability(
679
0
                                        Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
680
0
                                    ));
681
0
                                }
682
                            }
683
                            crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {
684
0
                                if !self
685
0
                                    .capabilities
686
0
                                    .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
687
                                {
688
0
                                    return Err(GlobalVariableError::UnsupportedCapability(
689
0
                                        Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
690
0
                                    ));
691
0
                                }
692
                            }
693
                            crate::ImageClass::External => {
694
                                // This should have been rejected in `validate_type`.
695
0
                                unreachable!("binding arrays of external images are not supported");
696
                            }
697
                        },
698
                        crate::TypeInner::Sampler { .. } => {
699
0
                            if !self
700
0
                                .capabilities
701
0
                                .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
702
                            {
703
0
                                return Err(GlobalVariableError::UnsupportedCapability(
704
0
                                    Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
705
0
                                ));
706
0
                            }
707
                        }
708
                        crate::TypeInner::AccelerationStructure { .. } => {
709
0
                            return Err(GlobalVariableError::InvalidBindingArray(base));
710
                        }
711
                        crate::TypeInner::RayQuery { .. } => {
712
                            // This should have been rejected in `validate_type`.
713
0
                            unreachable!("binding arrays of ray queries are not supported");
714
                        }
715
0
                        _ => {
716
0
                            // Fall through to the regular validation, which will reject `base`
717
0
                            // as invalid in `AddressSpace::Handle`.
718
0
                        }
719
                    }
720
0
                    base
721
                }
722
0
                _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
723
            },
724
0
            _ => var.ty,
725
        };
726
0
        let type_info = &self.types[inner_ty.index()];
727
728
0
        let (required_type_flags, is_resource) = match var.space {
729
            crate::AddressSpace::Function => {
730
0
                return Err(GlobalVariableError::InvalidUsage(var.space))
731
            }
732
0
            crate::AddressSpace::Storage { access } => {
733
0
                if let Err((ty_handle, disalignment)) = type_info.storage_layout {
734
0
                    if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
735
0
                        return Err(GlobalVariableError::Alignment(
736
0
                            var.space,
737
0
                            ty_handle,
738
0
                            disalignment,
739
0
                        ));
740
0
                    }
741
0
                }
742
0
                if access == crate::StorageAccess::STORE {
743
0
                    return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
744
0
                }
745
0
                (
746
0
                    TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,
747
0
                    true,
748
0
                )
749
            }
750
            crate::AddressSpace::Uniform => {
751
0
                if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
752
0
                    if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
753
0
                        return Err(GlobalVariableError::Alignment(
754
0
                            var.space,
755
0
                            ty_handle,
756
0
                            disalignment,
757
0
                        ));
758
0
                    }
759
0
                }
760
0
                (
761
0
                    TypeFlags::DATA
762
0
                        | TypeFlags::COPY
763
0
                        | TypeFlags::SIZED
764
0
                        | TypeFlags::HOST_SHAREABLE
765
0
                        | TypeFlags::CREATION_RESOLVED,
766
0
                    true,
767
0
                )
768
            }
769
            crate::AddressSpace::Handle => {
770
0
                match gctx.types[inner_ty].inner {
771
0
                    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
0
                            if !self
783
0
                                .capabilities
784
0
                                .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)
785
                            {
786
0
                                return Err(GlobalVariableError::UnsupportedCapability(
787
0
                                    Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
788
0
                                ));
789
0
                            }
790
                        }
791
0
                        _ => {}
792
                    },
793
                    crate::TypeInner::Sampler { .. }
794
                    | crate::TypeInner::AccelerationStructure { .. }
795
0
                    | crate::TypeInner::RayQuery { .. } => {}
796
                    _ => {
797
0
                        return Err(GlobalVariableError::InvalidType(var.space));
798
                    }
799
                }
800
801
0
                (TypeFlags::empty(), true)
802
            }
803
0
            crate::AddressSpace::Private => (
804
0
                TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,
805
0
                false,
806
0
            ),
807
0
            crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
808
            crate::AddressSpace::TaskPayload => {
809
0
                if !self.capabilities.contains(Capabilities::MESH_SHADER) {
810
0
                    return Err(GlobalVariableError::UnsupportedCapability(
811
0
                        Capabilities::MESH_SHADER,
812
0
                    ));
813
0
                }
814
0
                (TypeFlags::DATA | TypeFlags::SIZED, false)
815
            }
816
            crate::AddressSpace::Immediate => {
817
0
                if !self.capabilities.contains(Capabilities::IMMEDIATES) {
818
0
                    return Err(GlobalVariableError::UnsupportedCapability(
819
0
                        Capabilities::IMMEDIATES,
820
0
                    ));
821
0
                }
822
0
                if let Err(ref err) = type_info.immediates_compatibility {
823
0
                    return Err(GlobalVariableError::InvalidImmediateType(err.clone()));
824
0
                }
825
0
                (
826
0
                    TypeFlags::DATA
827
0
                        | TypeFlags::COPY
828
0
                        | TypeFlags::HOST_SHAREABLE
829
0
                        | TypeFlags::SIZED,
830
0
                    false,
831
0
                )
832
            }
833
        };
834
835
0
        if !type_info.flags.contains(required_type_flags) {
836
0
            return Err(GlobalVariableError::MissingTypeFlags {
837
0
                seen: type_info.flags,
838
0
                required: required_type_flags,
839
0
            });
840
0
        }
841
842
0
        if is_resource != var.binding.is_some() {
843
0
            if self.flags.contains(super::ValidationFlags::BINDINGS) {
844
0
                return Err(GlobalVariableError::InvalidBinding);
845
0
            }
846
0
        }
847
848
0
        if var.space == crate::AddressSpace::TaskPayload {
849
0
            let ty = &gctx.types[var.ty].inner;
850
            // HLSL doesn't allow zero sized payloads.
851
0
            if ty.try_size(gctx) == Some(0) {
852
0
                return Err(GlobalVariableError::ZeroSizedTaskPayload);
853
0
            }
854
0
        }
855
856
0
        if let Some(init) = var.init {
857
0
            match var.space {
858
0
                crate::AddressSpace::Private | crate::AddressSpace::Function => {}
859
                _ => {
860
0
                    return Err(GlobalVariableError::InitializerNotAllowed(var.space));
861
                }
862
            }
863
864
0
            if !global_expr_kind.is_const_or_override(init) {
865
0
                return Err(GlobalVariableError::InitializerExprType);
866
0
            }
867
868
0
            if !gctx.compare_types(
869
0
                &crate::proc::TypeResolution::Handle(var.ty),
870
0
                &mod_info[init],
871
0
            ) {
872
0
                return Err(GlobalVariableError::InitializerType);
873
0
            }
874
0
        }
875
876
0
        Ok(())
877
0
    }
878
879
    /// Validate the mesh shader output type `ty`, used as `mesh_output_type`.
880
0
    fn validate_mesh_output_type(
881
0
        &mut self,
882
0
        ep: &crate::EntryPoint,
883
0
        module: &crate::Module,
884
0
        ty: Handle<crate::Type>,
885
0
        mesh_output_type: MeshOutputType,
886
0
    ) -> Result<(), WithSpan<EntryPointError>> {
887
0
        if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {
888
0
            return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));
889
0
        }
890
0
        let mut result_built_ins = crate::FastHashSet::default();
891
0
        let mut ctx = VaryingContext {
892
0
            stage: ep.stage,
893
0
            output: true,
894
0
            types: &module.types,
895
0
            type_info: &self.types,
896
0
            location_mask: &mut self.location_mask,
897
0
            blend_src_mask: &mut self.blend_src_mask,
898
0
            built_ins: &mut result_built_ins,
899
0
            capabilities: self.capabilities,
900
0
            flags: self.flags,
901
0
            mesh_output_type,
902
0
            has_task_payload: ep.task_payload.is_some(),
903
0
        };
904
0
        ctx.validate(ep, ty, None)
905
0
            .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
906
0
        if mesh_output_type == MeshOutputType::PrimitiveOutput {
907
0
            let mut num_indices_builtins = 0;
908
0
            if result_built_ins.contains(&crate::BuiltIn::PointIndex) {
909
0
                num_indices_builtins += 1;
910
0
            }
911
0
            if result_built_ins.contains(&crate::BuiltIn::LineIndices) {
912
0
                num_indices_builtins += 1;
913
0
            }
914
0
            if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {
915
0
                num_indices_builtins += 1;
916
0
            }
917
0
            if num_indices_builtins != 1 {
918
0
                return Err(EntryPointError::InvalidMeshPrimitiveOutputType
919
0
                    .with_span_handle(ty, &module.types));
920
0
            }
921
0
        } else if mesh_output_type == MeshOutputType::VertexOutput
922
0
            && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
923
        {
924
0
            return Err(
925
0
                EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)
926
0
            );
927
0
        }
928
929
0
        Ok(())
930
0
    }
931
932
0
    pub(super) fn validate_entry_point(
933
0
        &mut self,
934
0
        ep: &crate::EntryPoint,
935
0
        module: &crate::Module,
936
0
        mod_info: &ModuleInfo,
937
0
    ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
938
0
        if matches!(
939
0
            ep.stage,
940
            crate::ShaderStage::Task | crate::ShaderStage::Mesh
941
0
        ) && !self.capabilities.contains(Capabilities::MESH_SHADER)
942
        {
943
0
            return Err(
944
0
                EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),
945
0
            );
946
0
        }
947
0
        if ep.early_depth_test.is_some() {
948
0
            let required = Capabilities::EARLY_DEPTH_TEST;
949
0
            if !self.capabilities.contains(required) {
950
0
                return Err(
951
0
                    EntryPointError::Result(VaryingError::UnsupportedCapability(required))
952
0
                        .with_span(),
953
0
                );
954
0
            }
955
956
0
            if ep.stage != crate::ShaderStage::Fragment {
957
0
                return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
958
0
            }
959
0
        }
960
961
0
        if ep.stage.compute_like() {
962
0
            if ep
963
0
                .workgroup_size
964
0
                .iter()
965
0
                .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
966
            {
967
0
                return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
968
0
            }
969
0
        } else if ep.workgroup_size != [0; 3] {
970
0
            return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
971
0
        }
972
973
0
        match (ep.stage, &ep.mesh_info) {
974
            (crate::ShaderStage::Mesh, &None) => {
975
0
                return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());
976
            }
977
0
            (crate::ShaderStage::Mesh, &Some(..)) => {}
978
            (_, &Some(_)) => {
979
0
                return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());
980
            }
981
0
            (_, _) => {}
982
        }
983
984
0
        let mut info = self
985
0
            .validate_function(&ep.function, module, mod_info, true)
986
0
            .map_err(WithSpan::into_other)?;
987
988
        // Validate the task shader payload.
989
0
        match ep.stage {
990
            // Task shaders must produce a payload.
991
            crate::ShaderStage::Task => {
992
0
                let Some(handle) = ep.task_payload else {
993
0
                    return Err(EntryPointError::ExpectedTaskPayload.with_span());
994
                };
995
0
                if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
996
0
                    return Err(EntryPointError::TaskPayloadWrongAddressSpace
997
0
                        .with_span_handle(handle, &module.global_variables));
998
0
                }
999
0
                info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);
1000
            }
1001
1002
            // Mesh shaders may accept a payload.
1003
            crate::ShaderStage::Mesh => {
1004
0
                if let Some(handle) = ep.task_payload {
1005
0
                    if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1006
0
                        return Err(EntryPointError::TaskPayloadWrongAddressSpace
1007
0
                            .with_span_handle(handle, &module.global_variables));
1008
0
                    }
1009
0
                    info.insert_global_use(GlobalUse::READ, handle);
1010
0
                }
1011
0
                if let Some(ref mesh_info) = ep.mesh_info {
1012
0
                    info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);
1013
0
                }
1014
            }
1015
1016
            // Other stages must not have a payload.
1017
            _ => {
1018
0
                if let Some(handle) = ep.task_payload {
1019
0
                    return Err(EntryPointError::UnexpectedTaskPayload
1020
0
                        .with_span_handle(handle, &module.global_variables));
1021
0
                }
1022
            }
1023
        }
1024
1025
        {
1026
            use super::ShaderStages;
1027
1028
0
            let stage_bit = match ep.stage {
1029
0
                crate::ShaderStage::Vertex => ShaderStages::VERTEX,
1030
0
                crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
1031
0
                crate::ShaderStage::Compute => ShaderStages::COMPUTE,
1032
0
                crate::ShaderStage::Mesh => ShaderStages::MESH,
1033
0
                crate::ShaderStage::Task => ShaderStages::TASK,
1034
            };
1035
1036
0
            if !info.available_stages.contains(stage_bit) {
1037
0
                return Err(EntryPointError::ForbiddenStageOperations.with_span());
1038
0
            }
1039
        }
1040
1041
0
        self.location_mask.clear();
1042
0
        let mut argument_built_ins = crate::FastHashSet::default();
1043
        // TODO: add span info to function arguments
1044
0
        for (index, fa) in ep.function.arguments.iter().enumerate() {
1045
0
            let mut ctx = VaryingContext {
1046
0
                stage: ep.stage,
1047
0
                output: false,
1048
0
                types: &module.types,
1049
0
                type_info: &self.types,
1050
0
                location_mask: &mut self.location_mask,
1051
0
                blend_src_mask: &mut self.blend_src_mask,
1052
0
                built_ins: &mut argument_built_ins,
1053
0
                capabilities: self.capabilities,
1054
0
                flags: self.flags,
1055
0
                mesh_output_type: MeshOutputType::None,
1056
0
                has_task_payload: ep.task_payload.is_some(),
1057
0
            };
1058
0
            ctx.validate(ep, fa.ty, fa.binding.as_ref())
1059
0
                .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
1060
        }
1061
1062
0
        self.location_mask.clear();
1063
0
        if let Some(ref fr) = ep.function.result {
1064
0
            let mut result_built_ins = crate::FastHashSet::default();
1065
0
            let mut ctx = VaryingContext {
1066
0
                stage: ep.stage,
1067
0
                output: true,
1068
0
                types: &module.types,
1069
0
                type_info: &self.types,
1070
0
                location_mask: &mut self.location_mask,
1071
0
                blend_src_mask: &mut self.blend_src_mask,
1072
0
                built_ins: &mut result_built_ins,
1073
0
                capabilities: self.capabilities,
1074
0
                flags: self.flags,
1075
0
                mesh_output_type: MeshOutputType::None,
1076
0
                has_task_payload: ep.task_payload.is_some(),
1077
0
            };
1078
0
            ctx.validate(ep, fr.ty, fr.binding.as_ref())
1079
0
                .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1080
0
            if ep.stage == crate::ShaderStage::Vertex
1081
0
                && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1082
            {
1083
0
                return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1084
0
            }
1085
0
            if ep.stage == crate::ShaderStage::Mesh {
1086
0
                return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span());
1087
0
            }
1088
            // Task shaders must have a single `MeshTaskSize` output, and nothing else.
1089
0
            if ep.stage == crate::ShaderStage::Task {
1090
0
                let ok = result_built_ins.contains(&crate::BuiltIn::MeshTaskSize)
1091
0
                    && result_built_ins.len() == 1
1092
0
                    && self.location_mask.is_empty();
1093
0
                if !ok {
1094
0
                    return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1095
0
                }
1096
0
            }
1097
0
            if !self.blend_src_mask.is_empty() {
1098
0
                info.dual_source_blending = true;
1099
0
            }
1100
0
        } else if ep.stage == crate::ShaderStage::Vertex {
1101
0
            return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1102
0
        } else if ep.stage == crate::ShaderStage::Task {
1103
0
            return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1104
0
        }
1105
1106
        {
1107
0
            let mut used_immediates = module
1108
0
                .global_variables
1109
0
                .iter()
1110
0
                .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
1111
0
                .map(|(handle, _)| handle)
1112
0
                .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
0
            if let Some(handle) = used_immediates.nth(1) {
1116
0
                return Err(EntryPointError::MoreThanOneImmediateUsed
1117
0
                    .with_span_handle(handle, &module.global_variables));
1118
0
            }
1119
        }
1120
1121
0
        self.ep_resource_bindings.clear();
1122
0
        for (var_handle, var) in module.global_variables.iter() {
1123
0
            let usage = info[var_handle];
1124
0
            if usage.is_empty() {
1125
0
                continue;
1126
0
            }
1127
1128
0
            if var.space == crate::AddressSpace::TaskPayload {
1129
0
                if ep.task_payload != Some(var_handle) {
1130
0
                    return Err(EntryPointError::WrongTaskPayloadUsed
1131
0
                        .with_span_handle(var_handle, &module.global_variables));
1132
0
                }
1133
0
                let size = module.types[var.ty].inner.size(module.to_ctx());
1134
0
                if size < 4 {
1135
0
                    return Err(EntryPointError::TaskPayloadTooSmall(size)
1136
0
                        .with_span_handle(var_handle, &module.global_variables));
1137
0
                }
1138
0
            }
1139
1140
0
            let allowed_usage = match var.space {
1141
0
                crate::AddressSpace::Function => unreachable!(),
1142
0
                crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
1143
0
                crate::AddressSpace::Storage { access } => storage_usage(access),
1144
0
                crate::AddressSpace::Handle => match module.types[var.ty].inner {
1145
0
                    crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
1146
                        crate::TypeInner::Image {
1147
0
                            class: crate::ImageClass::Storage { access, .. },
1148
                            ..
1149
0
                        } => storage_usage(access),
1150
0
                        _ => GlobalUse::READ | GlobalUse::QUERY,
1151
                    },
1152
                    crate::TypeInner::Image {
1153
0
                        class: crate::ImageClass::Storage { access, .. },
1154
                        ..
1155
0
                    } => storage_usage(access),
1156
0
                    _ => GlobalUse::READ | GlobalUse::QUERY,
1157
                },
1158
                crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {
1159
0
                    GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY
1160
                }
1161
                crate::AddressSpace::TaskPayload => {
1162
0
                    GlobalUse::READ
1163
0
                        | GlobalUse::QUERY
1164
0
                        | if ep.stage == crate::ShaderStage::Task {
1165
0
                            GlobalUse::WRITE
1166
                        } else {
1167
0
                            GlobalUse::empty()
1168
                        }
1169
                }
1170
0
                crate::AddressSpace::Immediate => GlobalUse::READ,
1171
            };
1172
0
            if !allowed_usage.contains(usage) {
1173
0
                log::warn!("\tUsage error for: {var:?}");
1174
0
                log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}");
1175
0
                return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
1176
0
                    .with_span_handle(var_handle, &module.global_variables));
1177
0
            }
1178
1179
0
            if let Some(ref bind) = var.binding {
1180
0
                if !self.ep_resource_bindings.insert(*bind) {
1181
0
                    if self.flags.contains(super::ValidationFlags::BINDINGS) {
1182
0
                        return Err(EntryPointError::BindingCollision(var_handle)
1183
0
                            .with_span_handle(var_handle, &module.global_variables));
1184
0
                    }
1185
0
                }
1186
0
            }
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
0
        if let &Some(ref mesh_info) = &ep.mesh_info {
1192
0
            if module.global_variables[mesh_info.output_variable].space
1193
0
                != crate::AddressSpace::WorkGroup
1194
            {
1195
0
                return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
1196
0
            }
1197
1198
0
            let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);
1199
0
            if let Some(e) = implied.2 {
1200
0
                return Err(e);
1201
0
            }
1202
1203
0
            if let Some(e) = mesh_info.max_vertices_override {
1204
0
                if let crate::Expression::Override(o) = module.global_expressions[e] {
1205
0
                    if implied.1[0] != Some(o) {
1206
0
                        return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1207
0
                    }
1208
0
                }
1209
0
            }
1210
0
            if let Some(e) = mesh_info.max_primitives_override {
1211
0
                if let crate::Expression::Override(o) = module.global_expressions[e] {
1212
0
                    if implied.1[1] != Some(o) {
1213
0
                        return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1214
0
                    }
1215
0
                }
1216
0
            }
1217
1218
0
            implied.0.max_vertices_override = mesh_info.max_vertices_override;
1219
0
            implied.0.max_primitives_override = mesh_info.max_primitives_override;
1220
0
            if implied.0 != *mesh_info {
1221
0
                return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1222
0
            }
1223
0
            if mesh_info.topology == crate::MeshOutputTopology::Points
1224
0
                && !self
1225
0
                    .capabilities
1226
0
                    .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)
1227
            {
1228
0
                return Err(EntryPointError::UnsupportedCapability(
1229
0
                    Capabilities::MESH_SHADER_POINT_TOPOLOGY,
1230
0
                )
1231
0
                .with_span());
1232
0
            }
1233
1234
0
            self.validate_mesh_output_type(
1235
0
                ep,
1236
0
                module,
1237
0
                mesh_info.vertex_output_type,
1238
0
                MeshOutputType::VertexOutput,
1239
0
            )?;
1240
0
            self.validate_mesh_output_type(
1241
0
                ep,
1242
0
                module,
1243
0
                mesh_info.primitive_output_type,
1244
0
                MeshOutputType::PrimitiveOutput,
1245
0
            )?;
1246
0
        }
1247
1248
0
        Ok(info)
1249
0
    }
1250
}