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 #[error("Memory decorations (`@coherent`, `@volatile`) are only valid for variables in the `storage` address space")]
51 InvalidMemoryDecorationsAddressSpace,
52 #[error("`@coherent` requires the MEMORY_DECORATION_COHERENT capability")]
53 CoherentNotSupported,
54 #[error("`@volatile` requires the MEMORY_DECORATION_VOLATILE capability")]
55 VolatileNotSupported,
56}
57
58#[derive(Clone, Debug, thiserror::Error)]
59#[cfg_attr(test, derive(PartialEq))]
60pub enum VaryingError {
61 #[error("The type {0:?} does not match the varying")]
62 InvalidType(Handle<crate::Type>),
63 #[error(
64 "The type {0:?} cannot be used for user-defined entry point inputs or outputs. \
65 Only numeric scalars and vectors are allowed."
66 )]
67 NotIOShareableType(Handle<crate::Type>),
68 #[error("Interpolation {0:?} is only valid for stage {1:?}")]
69 InvalidInterpolationInStage(crate::Interpolation, crate::ShaderStage),
70 #[error("Cannot combine {interpolation:?} interpolation with the {sampling:?} sample type")]
71 InvalidInterpolationSamplingCombination {
72 interpolation: crate::Interpolation,
73 sampling: crate::Sampling,
74 },
75 #[error("`@interpolate(flat)` must be explicitly specified for integer I/O")]
76 InvalidInterpolationForInteger,
77 #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")]
78 MissingInterpolation,
79 #[error("Built-in {0:?} is not available at this stage")]
80 InvalidBuiltInStage(crate::BuiltIn),
81 #[error("Built-in type for {0:?} is invalid. Found {1:?}")]
82 InvalidBuiltInType(crate::BuiltIn, crate::TypeInner),
83 #[error("Entry point arguments and return values must all have bindings")]
84 MissingBinding,
85 #[error("Struct member {0} is missing a binding")]
86 MemberMissingBinding(u32),
87 #[error("Multiple bindings at location {location} are present")]
88 BindingCollision { location: u32 },
89 #[error("Multiple bindings use the same `blend_src` {blend_src}")]
90 BindingCollisionBlendSrc { blend_src: u32 },
91 #[error("Built-in {0:?} is present more than once")]
92 DuplicateBuiltIn(crate::BuiltIn),
93 #[error("Capability {0:?} is not supported")]
94 UnsupportedCapability(Capabilities),
95 #[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
96 InvalidInputAttributeInStage(&'static str, crate::ShaderStage),
97 #[error("The attribute {0:?} is not valid for stage {1:?}")]
98 InvalidAttributeInStage(&'static str, crate::ShaderStage),
99 #[error("`@blend_src` can only be used at location 0, indices 0 and 1. Found `@location({location}) @blend_src({blend_src})`.")]
100 InvalidBlendSrcIndex { location: u32, blend_src: u32 },
101 #[error(
102 "`@blend_src` structure must specify two sources. \
103 Found `@blend_src({present_blend_src})` but not `@blend_src({absent_blend_src})`.",
104 absent_blend_src = if *present_blend_src == 0 { 1 } else { 0 },
105 )]
106 IncompleteBlendSrcUsage { present_blend_src: u32 },
107 #[error("Structure using `@blend_src` may not specify `@location` on any other members. Found a binding at `@location({location})`.")]
108 InvalidBlendSrcWithOtherBindings { location: u32 },
109 #[error("Both `@blend_src` structure members must have the same type. `blend_src(0)` has type {blend_src_0_type:?} and `blend_src(1)` has type {blend_src_1_type:?}.")]
110 BlendSrcOutputTypeMismatch {
111 blend_src_0_type: Handle<crate::Type>,
112 blend_src_1_type: Handle<crate::Type>,
113 },
114 #[error("`@blend_src` can only be used on struct members, not directly on entry point I/O")]
115 BlendSrcNotOnStructMember,
116 #[error("Workgroup size is multi dimensional, `@builtin(subgroup_id)` and `@builtin(subgroup_invocation_id)` are not supported.")]
117 InvalidMultiDimensionalSubgroupBuiltIn,
118 #[error("The `@per_primitive` attribute can only be used in fragment shader inputs or mesh shader primitive outputs")]
119 InvalidPerPrimitive,
120 #[error("Non-builtin members of a mesh primitive output struct must be decorated with `@per_primitive`")]
121 MissingPerPrimitive,
122 #[error("Per vertex fragment inputs must be an array of length 3.")]
123 PerVertexNotArrayOfThree,
124 #[error("Per vertex can only have Center sampling or no sampling modifier")]
125 InvalidPerVertexSampling,
126}
127
128#[derive(Clone, Debug, thiserror::Error)]
129#[cfg_attr(test, derive(PartialEq))]
130pub enum EntryPointError {
131 #[error("Multiple conflicting entry points")]
132 Conflict,
133 #[error("Vertex shaders must return a `@builtin(position)` output value")]
134 MissingVertexOutputPosition,
135 #[error("Early depth test is not applicable")]
136 UnexpectedEarlyDepthTest,
137 #[error("Workgroup size is not applicable")]
138 UnexpectedWorkgroupSize,
139 #[error("Workgroup size is out of range")]
140 OutOfRangeWorkgroupSize,
141 #[error("Uses operations forbidden at this stage")]
142 ForbiddenStageOperations,
143 #[error("Global variable {0:?} is used incorrectly as {1:?}")]
144 InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse),
145 #[error("More than 1 immediate data variable is used")]
146 MoreThanOneImmediateUsed,
147 #[error("Bindings for {0:?} conflict with other resource")]
148 BindingCollision(Handle<crate::GlobalVariable>),
149 #[error("Argument {0} varying error")]
150 Argument(u32, #[source] VaryingError),
151 #[error(transparent)]
152 Result(#[from] VaryingError),
153 #[error(transparent)]
154 Function(#[from] FunctionError),
155 #[error("Capability {0:?} is not supported")]
156 UnsupportedCapability(Capabilities),
157
158 #[error("mesh shader entry point missing mesh shader attributes")]
159 ExpectedMeshShaderAttributes,
160 #[error("Non mesh shader entry point cannot have mesh shader attributes")]
161 UnexpectedMeshShaderAttributes,
162 #[error("Non mesh/task shader entry point cannot have task payload attribute")]
163 UnexpectedTaskPayload,
164 #[error("Task payload must be declared with `var<task_payload>`")]
165 TaskPayloadWrongAddressSpace,
166 #[error("For a task payload to be used, it must be declared with @payload")]
167 WrongTaskPayloadUsed,
168 #[error("Task shader entry point must return @builtin(mesh_task_size) vec3<u32>")]
169 WrongTaskShaderEntryResult,
170 #[error("Task shaders must declare a task payload output")]
171 ExpectedTaskPayload,
172 #[error(
173 "Mesh shader output variable must be a struct with fields that are all allowed builtins"
174 )]
175 BadMeshOutputVariableType,
176 #[error("Mesh shader output variable fields must have types that are in accordance with the mesh shader spec")]
177 BadMeshOutputVariableField,
178 #[error("Mesh shader entry point cannot have a return type")]
179 UnexpectedMeshShaderEntryResult,
180 #[error(
181 "Mesh output type must be a user-defined struct with fields in alignment with the mesh shader spec"
182 )]
183 InvalidMeshOutputType,
184 #[error("Mesh primitive outputs must have exactly one of `@builtin(triangle_indices)`, `@builtin(line_indices)`, or `@builtin(point_index)`")]
185 InvalidMeshPrimitiveOutputType,
186 #[error("Mesh output global variable must live in the workgroup address space")]
187 WrongMeshOutputAddressSpace,
188 #[error("Task payload must be at least 4 bytes, but is {0} bytes")]
189 TaskPayloadTooSmall(u32),
190 #[error("Only the `ray_generation`, `closest_hit`, and `any_hit` shader stages can access a global variable in the `ray_payload` address space")]
191 RayPayloadInInvalidStage(crate::ShaderStage),
192 #[error("Only the `closest_hit`, `any_hit`, and `miss` shader stages can access a global variable in the `incoming_ray_payload` address space")]
193 IncomingRayPayloadInInvalidStage(crate::ShaderStage),
194 #[error("Compute shader entry point cannot have a return type")]
195 UnexpectedComputeShaderEntryResult,
196}
197
198fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
199 let mut storage_usage = GlobalUse::QUERY;
200 if access.contains(crate::StorageAccess::LOAD) {
201 storage_usage |= GlobalUse::READ;
202 }
203 if access.contains(crate::StorageAccess::STORE) {
204 storage_usage |= GlobalUse::WRITE;
205 }
206 if access.contains(crate::StorageAccess::ATOMIC) {
207 storage_usage |= GlobalUse::ATOMIC;
208 }
209 storage_usage
210}
211
212#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213enum MeshOutputType {
214 None,
215 VertexOutput,
216 PrimitiveOutput,
217}
218
219struct VaryingContext<'a> {
220 stage: crate::ShaderStage,
221 output: bool,
222 types: &'a UniqueArena<crate::Type>,
223 type_info: &'a Vec<super::r#type::TypeInfo>,
224 location_mask: &'a mut BitSet,
225 dual_source_blending: Option<&'a mut bool>,
226 built_ins: &'a mut crate::FastHashSet<crate::BuiltIn>,
227 capabilities: Capabilities,
228 flags: super::ValidationFlags,
229 mesh_output_type: MeshOutputType,
230 has_task_payload: bool,
231}
232
233impl VaryingContext<'_> {
234 fn validate_impl(
235 &mut self,
236 ep: &crate::EntryPoint,
237 ty: Handle<crate::Type>,
238 binding: &crate::Binding,
239 ) -> Result<(), VaryingError> {
240 use crate::{BuiltIn as Bi, ShaderStage as St, TypeInner as Ti, VectorSize as Vs};
241
242 let ty_inner = &self.types[ty].inner;
243 match *binding {
244 crate::Binding::BuiltIn(built_in) => {
245 let canonical = match built_in {
248 crate::BuiltIn::Position { .. } => {
249 crate::BuiltIn::Position { invariant: false }
250 }
251 crate::BuiltIn::Barycentric { .. } => {
252 crate::BuiltIn::Barycentric { perspective: false }
253 }
254 x => x,
255 };
256
257 if self.built_ins.contains(&canonical) {
258 return Err(VaryingError::DuplicateBuiltIn(built_in));
259 }
260 self.built_ins.insert(canonical);
261
262 let required = match built_in {
263 Bi::ClipDistances => Capabilities::CLIP_DISTANCES,
264 Bi::CullDistance => Capabilities::CULL_DISTANCE,
265 Bi::PrimitiveIndex if !matches!(ep.stage, St::AnyHit | St::ClosestHit) => {
267 Capabilities::PRIMITIVE_INDEX
268 }
269 Bi::Barycentric { .. } => Capabilities::SHADER_BARYCENTRICS,
270 Bi::ViewIndex => Capabilities::MULTIVIEW,
271 Bi::SampleIndex => Capabilities::MULTISAMPLED_SHADING,
272 Bi::NumSubgroups
273 | Bi::SubgroupId
274 | Bi::SubgroupSize
275 | Bi::SubgroupInvocationId => Capabilities::SUBGROUP,
276 Bi::DrawIndex => Capabilities::DRAW_INDEX,
277 _ => Capabilities::empty(),
278 };
279 if !self.capabilities.contains(required) {
280 return Err(VaryingError::UnsupportedCapability(required));
281 }
282
283 if matches!(
284 built_in,
285 crate::BuiltIn::SubgroupId | crate::BuiltIn::SubgroupInvocationId
286 ) && ep.workgroup_size[1..].iter().any(|&s| s > 1)
287 {
288 return Err(VaryingError::InvalidMultiDimensionalSubgroupBuiltIn);
289 }
290
291 let (visible, type_good) = match built_in {
292 Bi::BaseInstance | Bi::BaseVertex | Bi::VertexIndex => (
293 self.stage == St::Vertex && !self.output,
294 *ty_inner == Ti::Scalar(crate::Scalar::U32),
295 ),
296 Bi::InstanceIndex => (
297 matches!(self.stage, St::Vertex | St::AnyHit | St::ClosestHit)
298 && !self.output,
299 *ty_inner == Ti::Scalar(crate::Scalar::U32),
300 ),
301 Bi::DrawIndex => (
302 (self.stage == St::Vertex
304 || self.stage == St::Task
305 || (self.stage == St::Mesh && !self.has_task_payload))
306 && !self.output,
307 *ty_inner == Ti::Scalar(crate::Scalar::U32),
308 ),
309 Bi::ClipDistances | Bi::CullDistance => (
310 (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
311 match *ty_inner {
312 Ti::Array { base, size, .. } => {
313 self.types[base].inner == Ti::Scalar(crate::Scalar::F32)
314 && match size {
315 crate::ArraySize::Constant(non_zero) => non_zero.get() <= 8,
316 _ => false,
317 }
318 }
319 _ => false,
320 },
321 ),
322 Bi::PointSize => (
323 (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
324 *ty_inner == Ti::Scalar(crate::Scalar::F32),
325 ),
326 Bi::PointCoord => (
327 self.stage == St::Fragment && !self.output,
328 *ty_inner
329 == Ti::Vector {
330 size: Vs::Bi,
331 scalar: crate::Scalar::F32,
332 },
333 ),
334 Bi::Position { .. } => (
335 match self.stage {
336 St::Vertex | St::Mesh => self.output,
337 St::Fragment => !self.output,
338 St::Compute | St::Task => false,
339 St::RayGeneration | St::AnyHit | St::ClosestHit | St::Miss => false,
340 },
341 *ty_inner
342 == Ti::Vector {
343 size: Vs::Quad,
344 scalar: crate::Scalar::F32,
345 },
346 ),
347 Bi::ViewIndex => (
348 match self.stage {
349 St::Vertex | St::Fragment | St::Task | St::Mesh => !self.output,
350 St::Compute
351 | St::RayGeneration
352 | St::AnyHit
353 | St::ClosestHit
354 | St::Miss => false,
355 },
356 *ty_inner == Ti::Scalar(crate::Scalar::U32),
357 ),
358 Bi::FragDepth => (
359 self.stage == St::Fragment && self.output,
360 *ty_inner == Ti::Scalar(crate::Scalar::F32),
361 ),
362 Bi::FrontFacing => (
363 self.stage == St::Fragment && !self.output,
364 *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
365 ),
366 Bi::PrimitiveIndex => (
367 (matches!(self.stage, St::Fragment | St::AnyHit | St::ClosestHit)
368 && !self.output)
369 || (self.stage == St::Mesh
370 && self.output
371 && self.mesh_output_type == MeshOutputType::PrimitiveOutput),
372 *ty_inner == Ti::Scalar(crate::Scalar::U32),
373 ),
374 Bi::Barycentric { .. } => (
375 self.stage == St::Fragment && !self.output,
376 *ty_inner
377 == Ti::Vector {
378 size: Vs::Tri,
379 scalar: crate::Scalar::F32,
380 },
381 ),
382 Bi::SampleIndex => (
383 self.stage == St::Fragment && !self.output,
384 *ty_inner == Ti::Scalar(crate::Scalar::U32),
385 ),
386 Bi::SampleMask => (
387 self.stage == St::Fragment,
388 *ty_inner == Ti::Scalar(crate::Scalar::U32),
389 ),
390 Bi::LocalInvocationIndex => (
391 self.stage.compute_like() && !self.output,
392 *ty_inner == Ti::Scalar(crate::Scalar::U32),
393 ),
394 Bi::GlobalInvocationId
395 | Bi::LocalInvocationId
396 | Bi::WorkGroupId
397 | Bi::WorkGroupSize
398 | Bi::NumWorkGroups => (
399 self.stage.compute_like() && !self.output,
400 *ty_inner
401 == Ti::Vector {
402 size: Vs::Tri,
403 scalar: crate::Scalar::U32,
404 },
405 ),
406 Bi::NumSubgroups | Bi::SubgroupId => (
407 self.stage.compute_like() && !self.output,
408 *ty_inner == Ti::Scalar(crate::Scalar::U32),
409 ),
410 Bi::SubgroupSize | Bi::SubgroupInvocationId => (
411 match self.stage {
412 St::Compute
413 | St::Fragment
414 | St::Task
415 | St::Mesh
416 | St::RayGeneration
417 | St::AnyHit
418 | St::ClosestHit
419 | St::Miss => !self.output,
420 St::Vertex => false,
421 },
422 *ty_inner == Ti::Scalar(crate::Scalar::U32),
423 ),
424 Bi::CullPrimitive => (
425 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
426 *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
427 ),
428 Bi::PointIndex => (
429 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
430 *ty_inner == Ti::Scalar(crate::Scalar::U32),
431 ),
432 Bi::LineIndices => (
433 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
434 *ty_inner
435 == Ti::Vector {
436 size: Vs::Bi,
437 scalar: crate::Scalar::U32,
438 },
439 ),
440 Bi::TriangleIndices => (
441 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
442 *ty_inner
443 == Ti::Vector {
444 size: Vs::Tri,
445 scalar: crate::Scalar::U32,
446 },
447 ),
448 Bi::MeshTaskSize => (
449 self.stage == St::Task && self.output,
450 *ty_inner
451 == Ti::Vector {
452 size: Vs::Tri,
453 scalar: crate::Scalar::U32,
454 },
455 ),
456 Bi::RayInvocationId => (
457 match self.stage {
458 St::Vertex | St::Fragment | St::Compute | St::Mesh | St::Task => false,
459 St::RayGeneration | St::AnyHit | St::ClosestHit | St::Miss => true,
460 },
461 *ty_inner
462 == Ti::Vector {
463 size: Vs::Tri,
464 scalar: crate::Scalar::U32,
465 },
466 ),
467 Bi::NumRayInvocations => (
468 match self.stage {
469 St::Vertex | St::Fragment | St::Compute | St::Mesh | St::Task => false,
470 St::RayGeneration | St::AnyHit | St::ClosestHit | St::Miss => true,
471 },
472 *ty_inner
473 == Ti::Vector {
474 size: Vs::Tri,
475 scalar: crate::Scalar::U32,
476 },
477 ),
478 Bi::InstanceCustomData => (
479 match self.stage {
480 St::RayGeneration
481 | St::Miss
482 | St::Vertex
483 | St::Fragment
484 | St::Compute
485 | St::Mesh
486 | St::Task => false,
487 St::AnyHit | St::ClosestHit => true,
488 },
489 *ty_inner == Ti::Scalar(crate::Scalar::U32),
490 ),
491 Bi::GeometryIndex => (
492 match self.stage {
493 St::RayGeneration
494 | St::Miss
495 | St::Vertex
496 | St::Fragment
497 | St::Compute
498 | St::Mesh
499 | St::Task => false,
500 St::AnyHit | St::ClosestHit => true,
501 },
502 *ty_inner == Ti::Scalar(crate::Scalar::U32),
503 ),
504 Bi::WorldRayOrigin => (
505 match self.stage {
506 St::RayGeneration
507 | St::Vertex
508 | St::Fragment
509 | St::Compute
510 | St::Mesh
511 | St::Task => false,
512 St::AnyHit | St::ClosestHit | St::Miss => true,
513 },
514 *ty_inner
515 == Ti::Vector {
516 size: Vs::Tri,
517 scalar: crate::Scalar::F32,
518 },
519 ),
520 Bi::WorldRayDirection => (
521 match self.stage {
522 St::RayGeneration
523 | St::Vertex
524 | St::Fragment
525 | St::Compute
526 | St::Mesh
527 | St::Task => false,
528 St::AnyHit | St::ClosestHit | St::Miss => true,
529 },
530 *ty_inner
531 == Ti::Vector {
532 size: Vs::Tri,
533 scalar: crate::Scalar::F32,
534 },
535 ),
536 Bi::ObjectRayOrigin => (
537 match self.stage {
538 St::RayGeneration
539 | St::Miss
540 | St::Vertex
541 | St::Fragment
542 | St::Compute
543 | St::Mesh
544 | St::Task => false,
545 St::AnyHit | St::ClosestHit => true,
546 },
547 *ty_inner
548 == Ti::Vector {
549 size: Vs::Tri,
550 scalar: crate::Scalar::F32,
551 },
552 ),
553 Bi::ObjectRayDirection => (
554 match self.stage {
555 St::RayGeneration
556 | St::Miss
557 | St::Vertex
558 | St::Fragment
559 | St::Compute
560 | St::Mesh
561 | St::Task => false,
562 St::AnyHit | St::ClosestHit => true,
563 },
564 *ty_inner
565 == Ti::Vector {
566 size: Vs::Tri,
567 scalar: crate::Scalar::F32,
568 },
569 ),
570 Bi::RayTmin => (
571 match self.stage {
572 St::RayGeneration
573 | St::Vertex
574 | St::Fragment
575 | St::Compute
576 | St::Mesh
577 | St::Task => false,
578 St::AnyHit | St::ClosestHit | St::Miss => true,
579 },
580 *ty_inner == Ti::Scalar(crate::Scalar::F32),
581 ),
582 Bi::RayTCurrentMax => (
583 match self.stage {
584 St::RayGeneration
585 | St::Vertex
586 | St::Fragment
587 | St::Compute
588 | St::Mesh
589 | St::Task => false,
590 St::AnyHit | St::ClosestHit | St::Miss => true,
591 },
592 *ty_inner == Ti::Scalar(crate::Scalar::F32),
593 ),
594 Bi::ObjectToWorld => (
595 match self.stage {
596 St::RayGeneration
597 | St::Miss
598 | St::Vertex
599 | St::Fragment
600 | St::Compute
601 | St::Mesh
602 | St::Task => false,
603 St::AnyHit | St::ClosestHit => true,
604 },
605 *ty_inner
606 == Ti::Matrix {
607 columns: crate::VectorSize::Quad,
608 rows: crate::VectorSize::Tri,
609 scalar: crate::Scalar::F32,
610 },
611 ),
612 Bi::WorldToObject => (
613 match self.stage {
614 St::RayGeneration
615 | St::Miss
616 | St::Vertex
617 | St::Fragment
618 | St::Compute
619 | St::Mesh
620 | St::Task => false,
621 St::AnyHit | St::ClosestHit => true,
622 },
623 *ty_inner
624 == Ti::Matrix {
625 columns: crate::VectorSize::Quad,
626 rows: crate::VectorSize::Tri,
627 scalar: crate::Scalar::F32,
628 },
629 ),
630 Bi::HitKind => (
631 match self.stage {
632 St::RayGeneration
633 | St::Miss
634 | St::Vertex
635 | St::Fragment
636 | St::Compute
637 | St::Mesh
638 | St::Task => false,
639 St::AnyHit | St::ClosestHit => true,
640 },
641 *ty_inner == Ti::Scalar(crate::Scalar::U32),
642 ),
643 Bi::HitBarycentrics => (
644 match self.stage {
645 St::RayGeneration
646 | St::Miss
647 | St::Vertex
648 | St::Fragment
649 | St::Compute
650 | St::Mesh
651 | St::Task => false,
652 St::AnyHit | St::ClosestHit => !self.output,
653 },
654 *ty_inner
655 == Ti::Vector {
656 size: Vs::Bi,
657 scalar: crate::Scalar::F32,
658 },
659 ),
660 Bi::VertexCount | Bi::PrimitiveCount | Bi::Vertices | Bi::Primitives => {
662 (false, true)
663 }
664 };
665 match built_in {
666 Bi::CullPrimitive
667 | Bi::PointIndex
668 | Bi::LineIndices
669 | Bi::TriangleIndices
670 | Bi::MeshTaskSize
671 | Bi::VertexCount
672 | Bi::PrimitiveCount
673 | Bi::Vertices
674 | Bi::Primitives
675 if !self.capabilities.contains(Capabilities::MESH_SHADER) =>
676 {
677 return Err(VaryingError::UnsupportedCapability(
678 Capabilities::MESH_SHADER,
679 ));
680 }
681 _ => (),
682 }
683
684 if !visible {
685 return Err(VaryingError::InvalidBuiltInStage(built_in));
686 }
687 if !type_good {
688 return Err(VaryingError::InvalidBuiltInType(built_in, ty_inner.clone()));
689 }
690 }
691 crate::Binding::Location {
692 location,
693 interpolation,
694 sampling,
695 blend_src,
696 per_primitive,
697 } => {
698 if per_primitive && !self.capabilities.contains(Capabilities::MESH_SHADER) {
699 return Err(VaryingError::UnsupportedCapability(
700 Capabilities::MESH_SHADER,
701 ));
702 }
703 if interpolation == Some(crate::Interpolation::PerVertex) {
704 if self.stage != crate::ShaderStage::Fragment {
705 return Err(VaryingError::InvalidInterpolationInStage(
706 crate::Interpolation::PerVertex,
707 crate::ShaderStage::Fragment,
708 ));
709 }
710 if !self.capabilities.contains(Capabilities::PER_VERTEX) {
711 return Err(VaryingError::UnsupportedCapability(
712 Capabilities::PER_VERTEX,
713 ));
714 }
715 if sampling.is_some_and(|e| e != crate::Sampling::Center) {
716 return Err(VaryingError::InvalidPerVertexSampling);
717 }
718 }
719 let (ty, ty_inner) = if interpolation == Some(crate::Interpolation::PerVertex) {
722 let three = crate::ArraySize::Constant(core::num::NonZeroU32::new(3).unwrap());
723 match ty_inner {
724 &Ti::Array { base, size, .. } if size == three => {
725 (base, &self.types[base].inner)
726 }
727 _ => return Err(VaryingError::PerVertexNotArrayOfThree),
728 }
729 } else {
730 (ty, ty_inner)
731 };
732
733 if !self.type_info[ty.index()]
735 .flags
736 .contains(super::TypeFlags::IO_SHAREABLE)
737 {
738 return Err(VaryingError::NotIOShareableType(ty));
739 }
740
741 if self.mesh_output_type == MeshOutputType::PrimitiveOutput {
743 if !per_primitive {
745 return Err(VaryingError::MissingPerPrimitive);
746 }
747 } else if self.stage == crate::ShaderStage::Fragment && !self.output {
748 } else if per_primitive {
753 return Err(VaryingError::InvalidPerPrimitive);
755 }
756
757 if blend_src.is_some() {
758 return Err(VaryingError::BlendSrcNotOnStructMember);
759 } else if !self.location_mask.insert(location as usize)
760 && self.flags.contains(super::ValidationFlags::BINDINGS)
761 {
762 return Err(VaryingError::BindingCollision { location });
763 }
764
765 if let Some(interpolation) = interpolation {
766 let invalid_sampling = match (interpolation, sampling) {
767 (_, None)
768 | (
769 crate::Interpolation::Perspective | crate::Interpolation::Linear,
770 Some(
771 crate::Sampling::Center
772 | crate::Sampling::Centroid
773 | crate::Sampling::Sample,
774 ),
775 )
776 | (
777 crate::Interpolation::Flat,
778 Some(crate::Sampling::First | crate::Sampling::Either),
779 ) => None,
780 (_, Some(invalid_sampling)) => Some(invalid_sampling),
781 };
782 if let Some(sampling) = invalid_sampling {
783 return Err(VaryingError::InvalidInterpolationSamplingCombination {
784 interpolation,
785 sampling,
786 });
787 }
788 }
789
790 let needs_interpolation = match self.stage {
791 crate::ShaderStage::Vertex => self.output,
792 crate::ShaderStage::Fragment => !self.output && !per_primitive,
793 crate::ShaderStage::Compute
794 | crate::ShaderStage::Task
795 | crate::ShaderStage::RayGeneration
796 | crate::ShaderStage::AnyHit
797 | crate::ShaderStage::ClosestHit
798 | crate::ShaderStage::Miss => false,
799 crate::ShaderStage::Mesh => self.output,
800 };
801
802 let _ = sampling;
806
807 let mut required = match sampling {
808 Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
809 _ => Capabilities::empty(),
810 };
811 if interpolation == Some(crate::Interpolation::Linear) {
812 required |= Capabilities::LINEAR_INTERPOLATION;
813 }
814 if !self.capabilities.contains(required) {
815 return Err(VaryingError::UnsupportedCapability(
816 required - self.capabilities,
817 ));
818 }
819
820 if interpolation != Some(crate::Interpolation::PerVertex) {
821 match ty_inner.scalar_kind() {
822 Some(crate::ScalarKind::Float) => {
823 if needs_interpolation && interpolation.is_none() {
825 return Err(VaryingError::MissingInterpolation);
826 }
827 }
828 Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
829 if needs_interpolation
832 && interpolation != Some(crate::Interpolation::Flat)
833 {
834 return Err(VaryingError::InvalidInterpolationForInteger);
835 }
836 }
837 Some(_) | None => return Err(VaryingError::InvalidType(ty)),
838 }
839 }
840 }
841 }
842
843 Ok(())
844 }
845
846 fn validate(
847 &mut self,
848 ep: &crate::EntryPoint,
849 ty: Handle<crate::Type>,
850 binding: Option<&crate::Binding>,
851 ) -> Result<(), WithSpan<VaryingError>> {
852 let span_context = self.types.get_span_context(ty);
853 match binding {
854 Some(binding) => self
855 .validate_impl(ep, ty, binding)
856 .map_err(|e| e.with_span_context(span_context)),
857 None => {
858 let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {
859 if self.flags.contains(super::ValidationFlags::BINDINGS) {
860 return Err(VaryingError::MissingBinding.with_span());
861 } else {
862 return Ok(());
863 }
864 };
865
866 if self.type_info[ty.index()]
867 .flags
868 .contains(super::TypeFlags::IO_SHAREABLE)
869 {
870 if self.stage != crate::ShaderStage::Fragment {
874 return Err(
875 VaryingError::InvalidAttributeInStage("blend_src", self.stage)
876 .with_span(),
877 );
878 }
879 if !self.output {
880 return Err(VaryingError::InvalidInputAttributeInStage(
881 "blend_src",
882 self.stage,
883 )
884 .with_span());
885 }
886 if !self.location_mask.insert(0)
888 && self.flags.contains(super::ValidationFlags::BINDINGS)
889 {
890 return Err(VaryingError::BindingCollision { location: 0 }.with_span());
891 }
892
893 **self
894 .dual_source_blending
895 .as_mut()
896 .expect("unexpected dual source blending") = true;
897 } else {
898 for (index, member) in members.iter().enumerate() {
899 let span_context = self.types.get_span_context(ty);
900 match member.binding {
901 None => {
902 if self.flags.contains(super::ValidationFlags::BINDINGS) {
903 return Err(VaryingError::MemberMissingBinding(index as u32)
904 .with_span_context(span_context));
905 }
906 }
907 Some(ref binding) => self
908 .validate_impl(ep, member.ty, binding)
909 .map_err(|e| e.with_span_context(span_context))?,
910 }
911 }
912 }
913 Ok(())
914 }
915 }
916 }
917}
918
919impl super::Validator {
920 pub(super) fn validate_global_var(
921 &self,
922 var: &crate::GlobalVariable,
923 gctx: crate::proc::GlobalCtx,
924 mod_info: &ModuleInfo,
925 global_expr_kind: &crate::proc::ExpressionKindTracker,
926 ) -> Result<(), GlobalVariableError> {
927 use super::TypeFlags;
928
929 log::debug!("var {var:?}");
930 let inner_ty = match gctx.types[var.ty].inner {
931 crate::TypeInner::BindingArray { base, .. } => match var.space {
935 crate::AddressSpace::Storage { .. } => {
936 if !self
937 .capabilities
938 .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
939 {
940 return Err(GlobalVariableError::UnsupportedCapability(
941 Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
942 ));
943 }
944 base
945 }
946 crate::AddressSpace::Uniform => {
947 if !self
948 .capabilities
949 .contains(Capabilities::BUFFER_BINDING_ARRAY)
950 {
951 return Err(GlobalVariableError::UnsupportedCapability(
952 Capabilities::BUFFER_BINDING_ARRAY,
953 ));
954 }
955 base
956 }
957 crate::AddressSpace::Handle => {
958 match gctx.types[base].inner {
959 crate::TypeInner::Image { class, .. } => match class {
960 crate::ImageClass::Storage { .. } => {
961 if !self
962 .capabilities
963 .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
964 {
965 return Err(GlobalVariableError::UnsupportedCapability(
966 Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
967 ));
968 }
969 }
970 crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {
971 if !self
972 .capabilities
973 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
974 {
975 return Err(GlobalVariableError::UnsupportedCapability(
976 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
977 ));
978 }
979 }
980 crate::ImageClass::External => {
981 unreachable!("binding arrays of external images are not supported");
983 }
984 },
985 crate::TypeInner::Sampler { .. }
986 if !self
987 .capabilities
988 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY) =>
989 {
990 return Err(GlobalVariableError::UnsupportedCapability(
991 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
992 ));
993 }
994 crate::TypeInner::AccelerationStructure { .. }
995 if !self
996 .capabilities
997 .contains(Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY) =>
998 {
999 return Err(GlobalVariableError::UnsupportedCapability(
1000 Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY,
1001 ));
1002 }
1003 crate::TypeInner::RayQuery { .. } => {
1004 unreachable!("binding arrays of ray queries are not supported");
1006 }
1007 _ => {
1008 }
1011 }
1012 base
1013 }
1014 _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
1015 },
1016 _ => var.ty,
1017 };
1018 let type_info = &self.types[inner_ty.index()];
1019
1020 let (required_type_flags, is_resource) = match var.space {
1021 crate::AddressSpace::Function => {
1022 return Err(GlobalVariableError::InvalidUsage(var.space))
1023 }
1024 crate::AddressSpace::Storage { access } => {
1025 if let Err((ty_handle, disalignment)) = type_info.storage_layout {
1026 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1027 return Err(GlobalVariableError::Alignment(
1028 var.space,
1029 ty_handle,
1030 disalignment,
1031 ));
1032 }
1033 }
1034 if access == crate::StorageAccess::STORE {
1035 return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
1036 }
1037 (
1038 TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,
1039 true,
1040 )
1041 }
1042 crate::AddressSpace::Uniform => {
1043 if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
1044 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1045 return Err(GlobalVariableError::Alignment(
1046 var.space,
1047 ty_handle,
1048 disalignment,
1049 ));
1050 }
1051 }
1052 (
1053 TypeFlags::DATA
1054 | TypeFlags::COPY
1055 | TypeFlags::SIZED
1056 | TypeFlags::HOST_SHAREABLE
1057 | TypeFlags::CREATION_RESOLVED,
1058 true,
1059 )
1060 }
1061 crate::AddressSpace::Handle => {
1062 match gctx.types[inner_ty].inner {
1063 crate::TypeInner::Image { class, .. } => match class {
1064 crate::ImageClass::Storage {
1065 format:
1066 crate::StorageFormat::R16Unorm
1067 | crate::StorageFormat::R16Snorm
1068 | crate::StorageFormat::Rg16Unorm
1069 | crate::StorageFormat::Rg16Snorm
1070 | crate::StorageFormat::Rgba16Unorm
1071 | crate::StorageFormat::Rgba16Snorm,
1072 ..
1073 } if !self
1074 .capabilities
1075 .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS) =>
1076 {
1077 return Err(GlobalVariableError::UnsupportedCapability(
1078 Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
1079 ));
1080 }
1081 _ => {}
1082 },
1083 crate::TypeInner::Sampler { .. }
1084 | crate::TypeInner::AccelerationStructure { .. }
1085 | crate::TypeInner::RayQuery { .. } => {}
1086 _ => {
1087 return Err(GlobalVariableError::InvalidType(var.space));
1088 }
1089 }
1090
1091 (TypeFlags::empty(), true)
1092 }
1093 crate::AddressSpace::Private => (
1094 TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,
1095 false,
1096 ),
1097 crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
1098 crate::AddressSpace::TaskPayload => {
1099 if !self.capabilities.contains(Capabilities::MESH_SHADER) {
1100 return Err(GlobalVariableError::UnsupportedCapability(
1101 Capabilities::MESH_SHADER,
1102 ));
1103 }
1104 (TypeFlags::DATA | TypeFlags::SIZED, false)
1105 }
1106 crate::AddressSpace::Immediate => {
1107 if !self.capabilities.contains(Capabilities::IMMEDIATES) {
1108 return Err(GlobalVariableError::UnsupportedCapability(
1109 Capabilities::IMMEDIATES,
1110 ));
1111 }
1112 if let Err(ref err) = type_info.immediates_compatibility {
1113 return Err(GlobalVariableError::InvalidImmediateType(err.clone()));
1114 }
1115 (
1116 TypeFlags::DATA
1117 | TypeFlags::COPY
1118 | TypeFlags::HOST_SHAREABLE
1119 | TypeFlags::SIZED,
1120 false,
1121 )
1122 }
1123 crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
1124 if !self
1125 .capabilities
1126 .contains(Capabilities::RAY_TRACING_PIPELINE)
1127 {
1128 return Err(GlobalVariableError::UnsupportedCapability(
1129 Capabilities::RAY_TRACING_PIPELINE,
1130 ));
1131 }
1132 (TypeFlags::DATA | TypeFlags::SIZED, false)
1133 }
1134 };
1135
1136 if !type_info.flags.contains(required_type_flags) {
1137 return Err(GlobalVariableError::MissingTypeFlags {
1138 seen: type_info.flags,
1139 required: required_type_flags,
1140 });
1141 }
1142
1143 if is_resource != var.binding.is_some() {
1144 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1145 return Err(GlobalVariableError::InvalidBinding);
1146 }
1147 }
1148
1149 if var.space == crate::AddressSpace::TaskPayload {
1150 let ty = &gctx.types[var.ty].inner;
1151 if ty.try_size(gctx) == Some(0) {
1153 return Err(GlobalVariableError::ZeroSizedTaskPayload);
1154 }
1155 }
1156
1157 if !var.memory_decorations.is_empty()
1158 && !matches!(var.space, crate::AddressSpace::Storage { .. })
1159 {
1160 return Err(GlobalVariableError::InvalidMemoryDecorationsAddressSpace);
1161 }
1162 if var
1163 .memory_decorations
1164 .contains(crate::MemoryDecorations::COHERENT)
1165 && !self
1166 .capabilities
1167 .contains(Capabilities::MEMORY_DECORATION_COHERENT)
1168 {
1169 return Err(GlobalVariableError::CoherentNotSupported);
1170 }
1171 if var
1172 .memory_decorations
1173 .contains(crate::MemoryDecorations::VOLATILE)
1174 && !self
1175 .capabilities
1176 .contains(Capabilities::MEMORY_DECORATION_VOLATILE)
1177 {
1178 return Err(GlobalVariableError::VolatileNotSupported);
1179 }
1180
1181 if let Some(init) = var.init {
1182 match var.space {
1183 crate::AddressSpace::Private | crate::AddressSpace::Function => {}
1184 _ => {
1185 return Err(GlobalVariableError::InitializerNotAllowed(var.space));
1186 }
1187 }
1188
1189 if !global_expr_kind.is_const_or_override(init) {
1190 return Err(GlobalVariableError::InitializerExprType);
1191 }
1192
1193 if !gctx.compare_types(
1194 &crate::proc::TypeResolution::Handle(var.ty),
1195 &mod_info[init],
1196 ) {
1197 return Err(GlobalVariableError::InitializerType);
1198 }
1199 }
1200
1201 Ok(())
1202 }
1203
1204 fn validate_mesh_output_type(
1206 &mut self,
1207 ep: &crate::EntryPoint,
1208 module: &crate::Module,
1209 ty: Handle<crate::Type>,
1210 mesh_output_type: MeshOutputType,
1211 ) -> Result<(), WithSpan<EntryPointError>> {
1212 if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {
1213 return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));
1214 }
1215 let mut result_built_ins = crate::FastHashSet::default();
1216 let mut ctx = VaryingContext {
1217 stage: ep.stage,
1218 output: true,
1219 types: &module.types,
1220 type_info: &self.types,
1221 location_mask: &mut self.location_mask,
1222 dual_source_blending: None,
1223 built_ins: &mut result_built_ins,
1224 capabilities: self.capabilities,
1225 flags: self.flags,
1226 mesh_output_type,
1227 has_task_payload: ep.task_payload.is_some(),
1228 };
1229 ctx.validate(ep, ty, None)
1230 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1231 if mesh_output_type == MeshOutputType::PrimitiveOutput {
1232 let mut num_indices_builtins = 0;
1233 if result_built_ins.contains(&crate::BuiltIn::PointIndex) {
1234 num_indices_builtins += 1;
1235 }
1236 if result_built_ins.contains(&crate::BuiltIn::LineIndices) {
1237 num_indices_builtins += 1;
1238 }
1239 if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {
1240 num_indices_builtins += 1;
1241 }
1242 if num_indices_builtins != 1 {
1243 return Err(EntryPointError::InvalidMeshPrimitiveOutputType
1244 .with_span_handle(ty, &module.types));
1245 }
1246 } else if mesh_output_type == MeshOutputType::VertexOutput
1247 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1248 {
1249 return Err(
1250 EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)
1251 );
1252 }
1253
1254 Ok(())
1255 }
1256
1257 pub(super) fn validate_entry_point(
1258 &mut self,
1259 ep: &crate::EntryPoint,
1260 module: &crate::Module,
1261 mod_info: &ModuleInfo,
1262 ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
1263 match ep.stage {
1264 crate::ShaderStage::Task | crate::ShaderStage::Mesh
1265 if !self.capabilities.contains(Capabilities::MESH_SHADER) =>
1266 {
1267 return Err(
1268 EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),
1269 );
1270 }
1271 crate::ShaderStage::RayGeneration
1272 | crate::ShaderStage::AnyHit
1273 | crate::ShaderStage::ClosestHit
1274 | crate::ShaderStage::Miss
1275 if !self
1276 .capabilities
1277 .contains(Capabilities::RAY_TRACING_PIPELINE) =>
1278 {
1279 return Err(EntryPointError::UnsupportedCapability(
1280 Capabilities::RAY_TRACING_PIPELINE,
1281 )
1282 .with_span());
1283 }
1284 _ => {}
1285 }
1286 if ep.early_depth_test.is_some() {
1287 let required = Capabilities::EARLY_DEPTH_TEST;
1288 if !self.capabilities.contains(required) {
1289 return Err(
1290 EntryPointError::Result(VaryingError::UnsupportedCapability(required))
1291 .with_span(),
1292 );
1293 }
1294
1295 if ep.stage != crate::ShaderStage::Fragment {
1296 return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
1297 }
1298 }
1299
1300 if ep.stage.compute_like() {
1301 if ep
1302 .workgroup_size
1303 .iter()
1304 .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
1305 {
1306 return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
1307 }
1308 } else if ep.workgroup_size != [0; 3] {
1309 return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
1310 }
1311
1312 match (ep.stage, &ep.mesh_info) {
1313 (crate::ShaderStage::Mesh, &None) => {
1314 return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());
1315 }
1316 (crate::ShaderStage::Mesh, &Some(..)) => {}
1317 (_, &Some(_)) => {
1318 return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());
1319 }
1320 (_, _) => {}
1321 }
1322
1323 let mut info = self
1324 .validate_function(&ep.function, module, mod_info, true)
1325 .map_err(WithSpan::into_other)?;
1326
1327 match ep.stage {
1329 crate::ShaderStage::Task => {
1331 let Some(handle) = ep.task_payload else {
1332 return Err(EntryPointError::ExpectedTaskPayload.with_span());
1333 };
1334 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1335 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1336 .with_span_handle(handle, &module.global_variables));
1337 }
1338 info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);
1339 }
1340
1341 crate::ShaderStage::Mesh => {
1343 if let Some(handle) = ep.task_payload {
1344 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1345 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1346 .with_span_handle(handle, &module.global_variables));
1347 }
1348 info.insert_global_use(GlobalUse::READ, handle);
1349 }
1350 if let Some(ref mesh_info) = ep.mesh_info {
1351 info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);
1352 }
1353 }
1354
1355 _ => {
1357 if let Some(handle) = ep.task_payload {
1358 return Err(EntryPointError::UnexpectedTaskPayload
1359 .with_span_handle(handle, &module.global_variables));
1360 }
1361 }
1362 }
1363
1364 {
1365 use super::ShaderStages;
1366
1367 let stage_bit = match ep.stage {
1368 crate::ShaderStage::Vertex => ShaderStages::VERTEX,
1369 crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
1370 crate::ShaderStage::Compute => ShaderStages::COMPUTE,
1371 crate::ShaderStage::Mesh => ShaderStages::MESH,
1372 crate::ShaderStage::Task => ShaderStages::TASK,
1373 crate::ShaderStage::RayGeneration => ShaderStages::RAY_GENERATION,
1374 crate::ShaderStage::AnyHit => ShaderStages::ANY_HIT,
1375 crate::ShaderStage::ClosestHit => ShaderStages::CLOSEST_HIT,
1376 crate::ShaderStage::Miss => ShaderStages::MISS,
1377 };
1378
1379 if !info.available_stages.contains(stage_bit) {
1380 return Err(EntryPointError::ForbiddenStageOperations.with_span());
1381 }
1382 }
1383
1384 self.location_mask.make_empty();
1385 let mut argument_built_ins = crate::FastHashSet::default();
1386 for (index, fa) in ep.function.arguments.iter().enumerate() {
1388 let mut ctx = VaryingContext {
1389 stage: ep.stage,
1390 output: false,
1391 types: &module.types,
1392 type_info: &self.types,
1393 location_mask: &mut self.location_mask,
1394 dual_source_blending: Some(&mut info.dual_source_blending),
1395 built_ins: &mut argument_built_ins,
1396 capabilities: self.capabilities,
1397 flags: self.flags,
1398 mesh_output_type: MeshOutputType::None,
1399 has_task_payload: ep.task_payload.is_some(),
1400 };
1401 ctx.validate(ep, fa.ty, fa.binding.as_ref())
1402 .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
1403 match ep.stage {
1404 nt::ShaderStage::Compute | nt::ShaderStage::Mesh | nt::ShaderStage::Task => {
1405 let reject_location_binding = |binding| {
1406 if let Some(&crate::ir::Binding::Location { .. }) = binding {
1407 return Err(EntryPointError::Argument(
1408 index as u32,
1409 VaryingError::InvalidAttributeInStage("location", ep.stage),
1410 )
1411 .with_span());
1412 }
1413 Ok(())
1414 };
1415 reject_location_binding(fa.binding.as_ref())?;
1416
1417 if let &crate::TypeInner::Struct { ref members, .. } =
1418 &module.types[fa.ty].inner
1419 {
1420 members
1421 .iter()
1422 .map(|m| m.binding.as_ref())
1423 .try_for_each(reject_location_binding)?;
1424 }
1425 }
1426 nt::ShaderStage::Vertex
1427 | nt::ShaderStage::Fragment
1428 | nt::ShaderStage::RayGeneration
1429 | nt::ShaderStage::Miss
1430 | nt::ShaderStage::AnyHit
1431 | nt::ShaderStage::ClosestHit => {}
1432 }
1433 }
1434
1435 self.location_mask.make_empty();
1436 if let Some(ref fr) = ep.function.result {
1437 let mut result_built_ins = crate::FastHashSet::default();
1438 let mut ctx = VaryingContext {
1439 stage: ep.stage,
1440 output: true,
1441 types: &module.types,
1442 type_info: &self.types,
1443 location_mask: &mut self.location_mask,
1444 dual_source_blending: Some(&mut info.dual_source_blending),
1445 built_ins: &mut result_built_ins,
1446 capabilities: self.capabilities,
1447 flags: self.flags,
1448 mesh_output_type: MeshOutputType::None,
1449 has_task_payload: ep.task_payload.is_some(),
1450 };
1451 ctx.validate(ep, fr.ty, fr.binding.as_ref())
1452 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1453 match ep.stage {
1454 nt::ShaderStage::Vertex => {
1455 if !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false }) {
1456 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1457 }
1458 }
1459 nt::ShaderStage::Mesh => {
1460 return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span())
1461 }
1462 nt::ShaderStage::Task => {
1463 let ok = module.types[fr.ty].inner
1464 == crate::TypeInner::Vector {
1465 size: crate::VectorSize::Tri,
1466 scalar: crate::Scalar::U32,
1467 };
1468 if !ok {
1469 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1470 }
1471 }
1472 nt::ShaderStage::Compute => {
1473 return Err(EntryPointError::UnexpectedComputeShaderEntryResult.with_span())
1474 }
1475 nt::ShaderStage::Fragment
1476 | nt::ShaderStage::RayGeneration
1477 | nt::ShaderStage::Miss
1478 | nt::ShaderStage::AnyHit
1479 | nt::ShaderStage::ClosestHit => {}
1480 }
1481 } else {
1482 match ep.stage {
1483 nt::ShaderStage::Vertex => {
1484 return Err(EntryPointError::MissingVertexOutputPosition.with_span())
1485 }
1486 nt::ShaderStage::Task => {
1487 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span())
1488 }
1489 nt::ShaderStage::Mesh
1490 | nt::ShaderStage::Fragment
1491 | nt::ShaderStage::Compute
1492 | nt::ShaderStage::RayGeneration
1493 | nt::ShaderStage::Miss
1494 | nt::ShaderStage::AnyHit
1495 | nt::ShaderStage::ClosestHit => {}
1496 }
1497 }
1498
1499 {
1500 let mut used_immediates = module
1501 .global_variables
1502 .iter()
1503 .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
1504 .map(|(handle, _)| handle)
1505 .filter(|&handle| !info[handle].is_empty());
1506 if let Some(handle) = used_immediates.nth(1) {
1509 return Err(EntryPointError::MoreThanOneImmediateUsed
1510 .with_span_handle(handle, &module.global_variables));
1511 }
1512 }
1513
1514 self.ep_resource_bindings.clear();
1515 for (var_handle, var) in module.global_variables.iter() {
1516 let usage = info[var_handle];
1517 if usage.is_empty() {
1518 continue;
1519 }
1520
1521 if var.space == crate::AddressSpace::TaskPayload {
1522 if ep.task_payload != Some(var_handle) {
1523 return Err(EntryPointError::WrongTaskPayloadUsed
1524 .with_span_handle(var_handle, &module.global_variables));
1525 }
1526 let size = module.types[var.ty].inner.size(module.to_ctx());
1527 if size < 4 {
1528 return Err(EntryPointError::TaskPayloadTooSmall(size)
1529 .with_span_handle(var_handle, &module.global_variables));
1530 }
1531 }
1532
1533 let allowed_usage = match var.space {
1534 crate::AddressSpace::Function => unreachable!(),
1535 crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
1536 crate::AddressSpace::Storage { access } => storage_usage(access),
1537 crate::AddressSpace::Handle => match module.types[var.ty].inner {
1538 crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
1539 crate::TypeInner::Image {
1540 class: crate::ImageClass::Storage { access, .. },
1541 ..
1542 } => storage_usage(access),
1543 _ => GlobalUse::READ | GlobalUse::QUERY,
1544 },
1545 crate::TypeInner::Image {
1546 class: crate::ImageClass::Storage { access, .. },
1547 ..
1548 } => storage_usage(access),
1549 _ => GlobalUse::READ | GlobalUse::QUERY,
1550 },
1551 crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {
1552 GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY
1553 }
1554 crate::AddressSpace::TaskPayload => {
1555 GlobalUse::READ
1556 | GlobalUse::QUERY
1557 | if ep.stage == crate::ShaderStage::Task {
1558 GlobalUse::WRITE
1559 } else {
1560 GlobalUse::empty()
1561 }
1562 }
1563 crate::AddressSpace::Immediate => GlobalUse::READ,
1564 crate::AddressSpace::RayPayload => {
1565 if !matches!(
1566 ep.stage,
1567 crate::ShaderStage::RayGeneration
1568 | crate::ShaderStage::ClosestHit
1569 | crate::ShaderStage::Miss
1570 ) {
1571 return Err(EntryPointError::RayPayloadInInvalidStage(ep.stage)
1572 .with_span_handle(var_handle, &module.global_variables));
1573 }
1574 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1575 }
1576 crate::AddressSpace::IncomingRayPayload => {
1577 if !matches!(
1578 ep.stage,
1579 crate::ShaderStage::AnyHit
1580 | crate::ShaderStage::ClosestHit
1581 | crate::ShaderStage::Miss
1582 ) {
1583 return Err(EntryPointError::IncomingRayPayloadInInvalidStage(ep.stage)
1584 .with_span_handle(var_handle, &module.global_variables));
1585 }
1586 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1587 }
1588 };
1589 if !allowed_usage.contains(usage) {
1590 log::warn!("\tUsage error for: {var:?}");
1591 log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}");
1592 return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
1593 .with_span_handle(var_handle, &module.global_variables));
1594 }
1595
1596 if let Some(ref bind) = var.binding {
1597 if !self.ep_resource_bindings.insert(*bind) {
1598 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1599 return Err(EntryPointError::BindingCollision(var_handle)
1600 .with_span_handle(var_handle, &module.global_variables));
1601 }
1602 }
1603 }
1604 }
1605
1606 if let &Some(ref mesh_info) = &ep.mesh_info {
1609 if module.global_variables[mesh_info.output_variable].space
1610 != crate::AddressSpace::WorkGroup
1611 {
1612 return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
1613 }
1614
1615 let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);
1616 if let Some(e) = implied.2 {
1617 return Err(e);
1618 }
1619
1620 if let Some(e) = mesh_info.max_vertices_override {
1621 if let crate::Expression::Override(o) = module.global_expressions[e] {
1622 if implied.1[0] != Some(o) {
1623 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1624 }
1625 }
1626 }
1627 if let Some(e) = mesh_info.max_primitives_override {
1628 if let crate::Expression::Override(o) = module.global_expressions[e] {
1629 if implied.1[1] != Some(o) {
1630 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1631 }
1632 }
1633 }
1634
1635 implied.0.max_vertices_override = mesh_info.max_vertices_override;
1636 implied.0.max_primitives_override = mesh_info.max_primitives_override;
1637 if implied.0 != *mesh_info {
1638 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1639 }
1640 if mesh_info.topology == crate::MeshOutputTopology::Points
1641 && !self
1642 .capabilities
1643 .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)
1644 {
1645 return Err(EntryPointError::UnsupportedCapability(
1646 Capabilities::MESH_SHADER_POINT_TOPOLOGY,
1647 )
1648 .with_span());
1649 }
1650
1651 self.validate_mesh_output_type(
1652 ep,
1653 module,
1654 mesh_info.vertex_output_type,
1655 MeshOutputType::VertexOutput,
1656 )?;
1657 self.validate_mesh_output_type(
1658 ep,
1659 module,
1660 mesh_info.primitive_output_type,
1661 MeshOutputType::PrimitiveOutput,
1662 )?;
1663 }
1664
1665 Ok(info)
1666 }
1667}