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::VertexCount | Bi::PrimitiveCount | Bi::Vertices | Bi::Primitives => {
645 (false, true)
646 }
647 };
648 match built_in {
649 Bi::CullPrimitive
650 | Bi::PointIndex
651 | Bi::LineIndices
652 | Bi::TriangleIndices
653 | Bi::MeshTaskSize
654 | Bi::VertexCount
655 | Bi::PrimitiveCount
656 | Bi::Vertices
657 | Bi::Primitives => {
658 if !self.capabilities.contains(Capabilities::MESH_SHADER) {
659 return Err(VaryingError::UnsupportedCapability(
660 Capabilities::MESH_SHADER,
661 ));
662 }
663 }
664 _ => (),
665 }
666
667 if !visible {
668 return Err(VaryingError::InvalidBuiltInStage(built_in));
669 }
670 if !type_good {
671 return Err(VaryingError::InvalidBuiltInType(built_in, ty_inner.clone()));
672 }
673 }
674 crate::Binding::Location {
675 location,
676 interpolation,
677 sampling,
678 blend_src,
679 per_primitive,
680 } => {
681 if per_primitive && !self.capabilities.contains(Capabilities::MESH_SHADER) {
682 return Err(VaryingError::UnsupportedCapability(
683 Capabilities::MESH_SHADER,
684 ));
685 }
686 if interpolation == Some(crate::Interpolation::PerVertex) {
687 if self.stage != crate::ShaderStage::Fragment {
688 return Err(VaryingError::InvalidInterpolationInStage(
689 crate::Interpolation::PerVertex,
690 crate::ShaderStage::Fragment,
691 ));
692 }
693 if !self.capabilities.contains(Capabilities::PER_VERTEX) {
694 return Err(VaryingError::UnsupportedCapability(
695 Capabilities::PER_VERTEX,
696 ));
697 }
698 if sampling.is_some_and(|e| e != crate::Sampling::Center) {
699 return Err(VaryingError::InvalidPerVertexSampling);
700 }
701 }
702 let (ty, ty_inner) = if interpolation == Some(crate::Interpolation::PerVertex) {
705 let three = crate::ArraySize::Constant(core::num::NonZeroU32::new(3).unwrap());
706 match ty_inner {
707 &Ti::Array { base, size, .. } if size == three => {
708 (base, &self.types[base].inner)
709 }
710 _ => return Err(VaryingError::PerVertexNotArrayOfThree),
711 }
712 } else {
713 (ty, ty_inner)
714 };
715
716 if !self.type_info[ty.index()]
718 .flags
719 .contains(super::TypeFlags::IO_SHAREABLE)
720 {
721 return Err(VaryingError::NotIOShareableType(ty));
722 }
723
724 if self.mesh_output_type == MeshOutputType::PrimitiveOutput {
726 if !per_primitive {
728 return Err(VaryingError::MissingPerPrimitive);
729 }
730 } else if self.stage == crate::ShaderStage::Fragment && !self.output {
731 } else if per_primitive {
736 return Err(VaryingError::InvalidPerPrimitive);
738 }
739
740 if blend_src.is_some() {
741 return Err(VaryingError::BlendSrcNotOnStructMember);
742 } else if !self.location_mask.insert(location as usize)
743 && self.flags.contains(super::ValidationFlags::BINDINGS)
744 {
745 return Err(VaryingError::BindingCollision { location });
746 }
747
748 if let Some(interpolation) = interpolation {
749 let invalid_sampling = match (interpolation, sampling) {
750 (_, None)
751 | (
752 crate::Interpolation::Perspective | crate::Interpolation::Linear,
753 Some(
754 crate::Sampling::Center
755 | crate::Sampling::Centroid
756 | crate::Sampling::Sample,
757 ),
758 )
759 | (
760 crate::Interpolation::Flat,
761 Some(crate::Sampling::First | crate::Sampling::Either),
762 ) => None,
763 (_, Some(invalid_sampling)) => Some(invalid_sampling),
764 };
765 if let Some(sampling) = invalid_sampling {
766 return Err(VaryingError::InvalidInterpolationSamplingCombination {
767 interpolation,
768 sampling,
769 });
770 }
771 }
772
773 let needs_interpolation = match self.stage {
774 crate::ShaderStage::Vertex => self.output,
775 crate::ShaderStage::Fragment => !self.output && !per_primitive,
776 crate::ShaderStage::Compute
777 | crate::ShaderStage::Task
778 | crate::ShaderStage::RayGeneration
779 | crate::ShaderStage::AnyHit
780 | crate::ShaderStage::ClosestHit
781 | crate::ShaderStage::Miss => false,
782 crate::ShaderStage::Mesh => self.output,
783 };
784
785 let _ = sampling;
789
790 let mut required = match sampling {
791 Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
792 _ => Capabilities::empty(),
793 };
794 if interpolation == Some(crate::Interpolation::Linear) {
795 required |= Capabilities::LINEAR_INTERPOLATION;
796 }
797 if !self.capabilities.contains(required) {
798 return Err(VaryingError::UnsupportedCapability(
799 required - self.capabilities,
800 ));
801 }
802
803 if interpolation != Some(crate::Interpolation::PerVertex) {
804 match ty_inner.scalar_kind() {
805 Some(crate::ScalarKind::Float) => {
806 if needs_interpolation && interpolation.is_none() {
808 return Err(VaryingError::MissingInterpolation);
809 }
810 }
811 Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
812 if needs_interpolation
815 && interpolation != Some(crate::Interpolation::Flat)
816 {
817 return Err(VaryingError::InvalidInterpolationForInteger);
818 }
819 }
820 Some(_) | None => return Err(VaryingError::InvalidType(ty)),
821 }
822 }
823 }
824 }
825
826 Ok(())
827 }
828
829 fn validate(
830 &mut self,
831 ep: &crate::EntryPoint,
832 ty: Handle<crate::Type>,
833 binding: Option<&crate::Binding>,
834 ) -> Result<(), WithSpan<VaryingError>> {
835 let span_context = self.types.get_span_context(ty);
836 match binding {
837 Some(binding) => self
838 .validate_impl(ep, ty, binding)
839 .map_err(|e| e.with_span_context(span_context)),
840 None => {
841 let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {
842 if self.flags.contains(super::ValidationFlags::BINDINGS) {
843 return Err(VaryingError::MissingBinding.with_span());
844 } else {
845 return Ok(());
846 }
847 };
848
849 if self.type_info[ty.index()]
850 .flags
851 .contains(super::TypeFlags::IO_SHAREABLE)
852 {
853 if self.stage != crate::ShaderStage::Fragment {
857 return Err(
858 VaryingError::InvalidAttributeInStage("blend_src", self.stage)
859 .with_span(),
860 );
861 }
862 if !self.output {
863 return Err(VaryingError::InvalidInputAttributeInStage(
864 "blend_src",
865 self.stage,
866 )
867 .with_span());
868 }
869 if !self.location_mask.insert(0)
871 && self.flags.contains(super::ValidationFlags::BINDINGS)
872 {
873 return Err(VaryingError::BindingCollision { location: 0 }.with_span());
874 }
875
876 **self
877 .dual_source_blending
878 .as_mut()
879 .expect("unexpected dual source blending") = true;
880 } else {
881 for (index, member) in members.iter().enumerate() {
882 let span_context = self.types.get_span_context(ty);
883 match member.binding {
884 None => {
885 if self.flags.contains(super::ValidationFlags::BINDINGS) {
886 return Err(VaryingError::MemberMissingBinding(index as u32)
887 .with_span_context(span_context));
888 }
889 }
890 Some(ref binding) => self
891 .validate_impl(ep, member.ty, binding)
892 .map_err(|e| e.with_span_context(span_context))?,
893 }
894 }
895 }
896 Ok(())
897 }
898 }
899 }
900}
901
902impl super::Validator {
903 pub(super) fn validate_global_var(
904 &self,
905 var: &crate::GlobalVariable,
906 gctx: crate::proc::GlobalCtx,
907 mod_info: &ModuleInfo,
908 global_expr_kind: &crate::proc::ExpressionKindTracker,
909 ) -> Result<(), GlobalVariableError> {
910 use super::TypeFlags;
911
912 log::debug!("var {var:?}");
913 let inner_ty = match gctx.types[var.ty].inner {
914 crate::TypeInner::BindingArray { base, .. } => match var.space {
918 crate::AddressSpace::Storage { .. } => {
919 if !self
920 .capabilities
921 .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
922 {
923 return Err(GlobalVariableError::UnsupportedCapability(
924 Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
925 ));
926 }
927 base
928 }
929 crate::AddressSpace::Uniform => {
930 if !self
931 .capabilities
932 .contains(Capabilities::BUFFER_BINDING_ARRAY)
933 {
934 return Err(GlobalVariableError::UnsupportedCapability(
935 Capabilities::BUFFER_BINDING_ARRAY,
936 ));
937 }
938 base
939 }
940 crate::AddressSpace::Handle => {
941 match gctx.types[base].inner {
942 crate::TypeInner::Image { class, .. } => match class {
943 crate::ImageClass::Storage { .. } => {
944 if !self
945 .capabilities
946 .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
947 {
948 return Err(GlobalVariableError::UnsupportedCapability(
949 Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
950 ));
951 }
952 }
953 crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {
954 if !self
955 .capabilities
956 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
957 {
958 return Err(GlobalVariableError::UnsupportedCapability(
959 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
960 ));
961 }
962 }
963 crate::ImageClass::External => {
964 unreachable!("binding arrays of external images are not supported");
966 }
967 },
968 crate::TypeInner::Sampler { .. } => {
969 if !self
970 .capabilities
971 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
972 {
973 return Err(GlobalVariableError::UnsupportedCapability(
974 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
975 ));
976 }
977 }
978 crate::TypeInner::AccelerationStructure { .. } => {
979 if !self
980 .capabilities
981 .contains(Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY)
982 {
983 return Err(GlobalVariableError::UnsupportedCapability(
984 Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY,
985 ));
986 }
987 }
988 crate::TypeInner::RayQuery { .. } => {
989 unreachable!("binding arrays of ray queries are not supported");
991 }
992 _ => {
993 }
996 }
997 base
998 }
999 _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
1000 },
1001 _ => var.ty,
1002 };
1003 let type_info = &self.types[inner_ty.index()];
1004
1005 let (required_type_flags, is_resource) = match var.space {
1006 crate::AddressSpace::Function => {
1007 return Err(GlobalVariableError::InvalidUsage(var.space))
1008 }
1009 crate::AddressSpace::Storage { access } => {
1010 if let Err((ty_handle, disalignment)) = type_info.storage_layout {
1011 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1012 return Err(GlobalVariableError::Alignment(
1013 var.space,
1014 ty_handle,
1015 disalignment,
1016 ));
1017 }
1018 }
1019 if access == crate::StorageAccess::STORE {
1020 return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
1021 }
1022 (
1023 TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,
1024 true,
1025 )
1026 }
1027 crate::AddressSpace::Uniform => {
1028 if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
1029 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1030 return Err(GlobalVariableError::Alignment(
1031 var.space,
1032 ty_handle,
1033 disalignment,
1034 ));
1035 }
1036 }
1037 (
1038 TypeFlags::DATA
1039 | TypeFlags::COPY
1040 | TypeFlags::SIZED
1041 | TypeFlags::HOST_SHAREABLE
1042 | TypeFlags::CREATION_RESOLVED,
1043 true,
1044 )
1045 }
1046 crate::AddressSpace::Handle => {
1047 match gctx.types[inner_ty].inner {
1048 crate::TypeInner::Image { class, .. } => match class {
1049 crate::ImageClass::Storage {
1050 format:
1051 crate::StorageFormat::R16Unorm
1052 | crate::StorageFormat::R16Snorm
1053 | crate::StorageFormat::Rg16Unorm
1054 | crate::StorageFormat::Rg16Snorm
1055 | crate::StorageFormat::Rgba16Unorm
1056 | crate::StorageFormat::Rgba16Snorm,
1057 ..
1058 } => {
1059 if !self
1060 .capabilities
1061 .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)
1062 {
1063 return Err(GlobalVariableError::UnsupportedCapability(
1064 Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
1065 ));
1066 }
1067 }
1068 _ => {}
1069 },
1070 crate::TypeInner::Sampler { .. }
1071 | crate::TypeInner::AccelerationStructure { .. }
1072 | crate::TypeInner::RayQuery { .. } => {}
1073 _ => {
1074 return Err(GlobalVariableError::InvalidType(var.space));
1075 }
1076 }
1077
1078 (TypeFlags::empty(), true)
1079 }
1080 crate::AddressSpace::Private => (
1081 TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,
1082 false,
1083 ),
1084 crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
1085 crate::AddressSpace::TaskPayload => {
1086 if !self.capabilities.contains(Capabilities::MESH_SHADER) {
1087 return Err(GlobalVariableError::UnsupportedCapability(
1088 Capabilities::MESH_SHADER,
1089 ));
1090 }
1091 (TypeFlags::DATA | TypeFlags::SIZED, false)
1092 }
1093 crate::AddressSpace::Immediate => {
1094 if !self.capabilities.contains(Capabilities::IMMEDIATES) {
1095 return Err(GlobalVariableError::UnsupportedCapability(
1096 Capabilities::IMMEDIATES,
1097 ));
1098 }
1099 if let Err(ref err) = type_info.immediates_compatibility {
1100 return Err(GlobalVariableError::InvalidImmediateType(err.clone()));
1101 }
1102 (
1103 TypeFlags::DATA
1104 | TypeFlags::COPY
1105 | TypeFlags::HOST_SHAREABLE
1106 | TypeFlags::SIZED,
1107 false,
1108 )
1109 }
1110 crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
1111 if !self
1112 .capabilities
1113 .contains(Capabilities::RAY_TRACING_PIPELINE)
1114 {
1115 return Err(GlobalVariableError::UnsupportedCapability(
1116 Capabilities::RAY_TRACING_PIPELINE,
1117 ));
1118 }
1119 (TypeFlags::DATA | TypeFlags::SIZED, false)
1120 }
1121 };
1122
1123 if !type_info.flags.contains(required_type_flags) {
1124 return Err(GlobalVariableError::MissingTypeFlags {
1125 seen: type_info.flags,
1126 required: required_type_flags,
1127 });
1128 }
1129
1130 if is_resource != var.binding.is_some() {
1131 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1132 return Err(GlobalVariableError::InvalidBinding);
1133 }
1134 }
1135
1136 if var.space == crate::AddressSpace::TaskPayload {
1137 let ty = &gctx.types[var.ty].inner;
1138 if ty.try_size(gctx) == Some(0) {
1140 return Err(GlobalVariableError::ZeroSizedTaskPayload);
1141 }
1142 }
1143
1144 if !var.memory_decorations.is_empty()
1145 && !matches!(var.space, crate::AddressSpace::Storage { .. })
1146 {
1147 return Err(GlobalVariableError::InvalidMemoryDecorationsAddressSpace);
1148 }
1149 if var
1150 .memory_decorations
1151 .contains(crate::MemoryDecorations::COHERENT)
1152 && !self
1153 .capabilities
1154 .contains(Capabilities::MEMORY_DECORATION_COHERENT)
1155 {
1156 return Err(GlobalVariableError::CoherentNotSupported);
1157 }
1158 if var
1159 .memory_decorations
1160 .contains(crate::MemoryDecorations::VOLATILE)
1161 && !self
1162 .capabilities
1163 .contains(Capabilities::MEMORY_DECORATION_VOLATILE)
1164 {
1165 return Err(GlobalVariableError::VolatileNotSupported);
1166 }
1167
1168 if let Some(init) = var.init {
1169 match var.space {
1170 crate::AddressSpace::Private | crate::AddressSpace::Function => {}
1171 _ => {
1172 return Err(GlobalVariableError::InitializerNotAllowed(var.space));
1173 }
1174 }
1175
1176 if !global_expr_kind.is_const_or_override(init) {
1177 return Err(GlobalVariableError::InitializerExprType);
1178 }
1179
1180 if !gctx.compare_types(
1181 &crate::proc::TypeResolution::Handle(var.ty),
1182 &mod_info[init],
1183 ) {
1184 return Err(GlobalVariableError::InitializerType);
1185 }
1186 }
1187
1188 Ok(())
1189 }
1190
1191 fn validate_mesh_output_type(
1193 &mut self,
1194 ep: &crate::EntryPoint,
1195 module: &crate::Module,
1196 ty: Handle<crate::Type>,
1197 mesh_output_type: MeshOutputType,
1198 ) -> Result<(), WithSpan<EntryPointError>> {
1199 if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {
1200 return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));
1201 }
1202 let mut result_built_ins = crate::FastHashSet::default();
1203 let mut ctx = VaryingContext {
1204 stage: ep.stage,
1205 output: true,
1206 types: &module.types,
1207 type_info: &self.types,
1208 location_mask: &mut self.location_mask,
1209 dual_source_blending: None,
1210 built_ins: &mut result_built_ins,
1211 capabilities: self.capabilities,
1212 flags: self.flags,
1213 mesh_output_type,
1214 has_task_payload: ep.task_payload.is_some(),
1215 };
1216 ctx.validate(ep, ty, None)
1217 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1218 if mesh_output_type == MeshOutputType::PrimitiveOutput {
1219 let mut num_indices_builtins = 0;
1220 if result_built_ins.contains(&crate::BuiltIn::PointIndex) {
1221 num_indices_builtins += 1;
1222 }
1223 if result_built_ins.contains(&crate::BuiltIn::LineIndices) {
1224 num_indices_builtins += 1;
1225 }
1226 if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {
1227 num_indices_builtins += 1;
1228 }
1229 if num_indices_builtins != 1 {
1230 return Err(EntryPointError::InvalidMeshPrimitiveOutputType
1231 .with_span_handle(ty, &module.types));
1232 }
1233 } else if mesh_output_type == MeshOutputType::VertexOutput
1234 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1235 {
1236 return Err(
1237 EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)
1238 );
1239 }
1240
1241 Ok(())
1242 }
1243
1244 pub(super) fn validate_entry_point(
1245 &mut self,
1246 ep: &crate::EntryPoint,
1247 module: &crate::Module,
1248 mod_info: &ModuleInfo,
1249 ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
1250 match ep.stage {
1251 crate::ShaderStage::Task | crate::ShaderStage::Mesh
1252 if !self.capabilities.contains(Capabilities::MESH_SHADER) =>
1253 {
1254 return Err(
1255 EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),
1256 );
1257 }
1258 crate::ShaderStage::RayGeneration
1259 | crate::ShaderStage::AnyHit
1260 | crate::ShaderStage::ClosestHit
1261 | crate::ShaderStage::Miss
1262 if !self
1263 .capabilities
1264 .contains(Capabilities::RAY_TRACING_PIPELINE) =>
1265 {
1266 return Err(EntryPointError::UnsupportedCapability(
1267 Capabilities::RAY_TRACING_PIPELINE,
1268 )
1269 .with_span());
1270 }
1271 _ => {}
1272 }
1273 if ep.early_depth_test.is_some() {
1274 let required = Capabilities::EARLY_DEPTH_TEST;
1275 if !self.capabilities.contains(required) {
1276 return Err(
1277 EntryPointError::Result(VaryingError::UnsupportedCapability(required))
1278 .with_span(),
1279 );
1280 }
1281
1282 if ep.stage != crate::ShaderStage::Fragment {
1283 return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
1284 }
1285 }
1286
1287 if ep.stage.compute_like() {
1288 if ep
1289 .workgroup_size
1290 .iter()
1291 .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
1292 {
1293 return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
1294 }
1295 } else if ep.workgroup_size != [0; 3] {
1296 return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
1297 }
1298
1299 match (ep.stage, &ep.mesh_info) {
1300 (crate::ShaderStage::Mesh, &None) => {
1301 return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());
1302 }
1303 (crate::ShaderStage::Mesh, &Some(..)) => {}
1304 (_, &Some(_)) => {
1305 return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());
1306 }
1307 (_, _) => {}
1308 }
1309
1310 let mut info = self
1311 .validate_function(&ep.function, module, mod_info, true)
1312 .map_err(WithSpan::into_other)?;
1313
1314 match ep.stage {
1316 crate::ShaderStage::Task => {
1318 let Some(handle) = ep.task_payload else {
1319 return Err(EntryPointError::ExpectedTaskPayload.with_span());
1320 };
1321 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1322 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1323 .with_span_handle(handle, &module.global_variables));
1324 }
1325 info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);
1326 }
1327
1328 crate::ShaderStage::Mesh => {
1330 if let Some(handle) = ep.task_payload {
1331 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1332 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1333 .with_span_handle(handle, &module.global_variables));
1334 }
1335 info.insert_global_use(GlobalUse::READ, handle);
1336 }
1337 if let Some(ref mesh_info) = ep.mesh_info {
1338 info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);
1339 }
1340 }
1341
1342 _ => {
1344 if let Some(handle) = ep.task_payload {
1345 return Err(EntryPointError::UnexpectedTaskPayload
1346 .with_span_handle(handle, &module.global_variables));
1347 }
1348 }
1349 }
1350
1351 {
1352 use super::ShaderStages;
1353
1354 let stage_bit = match ep.stage {
1355 crate::ShaderStage::Vertex => ShaderStages::VERTEX,
1356 crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
1357 crate::ShaderStage::Compute => ShaderStages::COMPUTE,
1358 crate::ShaderStage::Mesh => ShaderStages::MESH,
1359 crate::ShaderStage::Task => ShaderStages::TASK,
1360 crate::ShaderStage::RayGeneration => ShaderStages::RAY_GENERATION,
1361 crate::ShaderStage::AnyHit => ShaderStages::ANY_HIT,
1362 crate::ShaderStage::ClosestHit => ShaderStages::CLOSEST_HIT,
1363 crate::ShaderStage::Miss => ShaderStages::MISS,
1364 };
1365
1366 if !info.available_stages.contains(stage_bit) {
1367 return Err(EntryPointError::ForbiddenStageOperations.with_span());
1368 }
1369 }
1370
1371 self.location_mask.make_empty();
1372 let mut argument_built_ins = crate::FastHashSet::default();
1373 for (index, fa) in ep.function.arguments.iter().enumerate() {
1375 let mut ctx = VaryingContext {
1376 stage: ep.stage,
1377 output: false,
1378 types: &module.types,
1379 type_info: &self.types,
1380 location_mask: &mut self.location_mask,
1381 dual_source_blending: Some(&mut info.dual_source_blending),
1382 built_ins: &mut argument_built_ins,
1383 capabilities: self.capabilities,
1384 flags: self.flags,
1385 mesh_output_type: MeshOutputType::None,
1386 has_task_payload: ep.task_payload.is_some(),
1387 };
1388 ctx.validate(ep, fa.ty, fa.binding.as_ref())
1389 .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
1390 match ep.stage {
1391 nt::ShaderStage::Compute | nt::ShaderStage::Mesh | nt::ShaderStage::Task => {
1392 let reject_location_binding = |binding| {
1393 if let Some(&crate::ir::Binding::Location { .. }) = binding {
1394 return Err(EntryPointError::Argument(
1395 index as u32,
1396 VaryingError::InvalidAttributeInStage("location", ep.stage),
1397 )
1398 .with_span());
1399 }
1400 Ok(())
1401 };
1402 reject_location_binding(fa.binding.as_ref())?;
1403
1404 if let &crate::TypeInner::Struct { ref members, .. } =
1405 &module.types[fa.ty].inner
1406 {
1407 members
1408 .iter()
1409 .map(|m| m.binding.as_ref())
1410 .try_for_each(reject_location_binding)?;
1411 }
1412 }
1413 nt::ShaderStage::Vertex
1414 | nt::ShaderStage::Fragment
1415 | nt::ShaderStage::RayGeneration
1416 | nt::ShaderStage::Miss
1417 | nt::ShaderStage::AnyHit
1418 | nt::ShaderStage::ClosestHit => {}
1419 }
1420 }
1421
1422 self.location_mask.make_empty();
1423 if let Some(ref fr) = ep.function.result {
1424 let mut result_built_ins = crate::FastHashSet::default();
1425 let mut ctx = VaryingContext {
1426 stage: ep.stage,
1427 output: true,
1428 types: &module.types,
1429 type_info: &self.types,
1430 location_mask: &mut self.location_mask,
1431 dual_source_blending: Some(&mut info.dual_source_blending),
1432 built_ins: &mut result_built_ins,
1433 capabilities: self.capabilities,
1434 flags: self.flags,
1435 mesh_output_type: MeshOutputType::None,
1436 has_task_payload: ep.task_payload.is_some(),
1437 };
1438 ctx.validate(ep, fr.ty, fr.binding.as_ref())
1439 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1440 match ep.stage {
1441 nt::ShaderStage::Vertex => {
1442 if !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false }) {
1443 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1444 }
1445 }
1446 nt::ShaderStage::Mesh => {
1447 return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span())
1448 }
1449 nt::ShaderStage::Task => {
1450 let ok = module.types[fr.ty].inner
1451 == crate::TypeInner::Vector {
1452 size: crate::VectorSize::Tri,
1453 scalar: crate::Scalar::U32,
1454 };
1455 if !ok {
1456 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1457 }
1458 }
1459 nt::ShaderStage::Compute => {
1460 return Err(EntryPointError::UnexpectedComputeShaderEntryResult.with_span())
1461 }
1462 nt::ShaderStage::Fragment
1463 | nt::ShaderStage::RayGeneration
1464 | nt::ShaderStage::Miss
1465 | nt::ShaderStage::AnyHit
1466 | nt::ShaderStage::ClosestHit => {}
1467 }
1468 } else {
1469 match ep.stage {
1470 nt::ShaderStage::Vertex => {
1471 return Err(EntryPointError::MissingVertexOutputPosition.with_span())
1472 }
1473 nt::ShaderStage::Task => {
1474 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span())
1475 }
1476 nt::ShaderStage::Mesh
1477 | nt::ShaderStage::Fragment
1478 | nt::ShaderStage::Compute
1479 | nt::ShaderStage::RayGeneration
1480 | nt::ShaderStage::Miss
1481 | nt::ShaderStage::AnyHit
1482 | nt::ShaderStage::ClosestHit => {}
1483 }
1484 }
1485
1486 {
1487 let mut used_immediates = module
1488 .global_variables
1489 .iter()
1490 .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
1491 .map(|(handle, _)| handle)
1492 .filter(|&handle| !info[handle].is_empty());
1493 if let Some(handle) = used_immediates.nth(1) {
1496 return Err(EntryPointError::MoreThanOneImmediateUsed
1497 .with_span_handle(handle, &module.global_variables));
1498 }
1499 }
1500
1501 self.ep_resource_bindings.clear();
1502 for (var_handle, var) in module.global_variables.iter() {
1503 let usage = info[var_handle];
1504 if usage.is_empty() {
1505 continue;
1506 }
1507
1508 if var.space == crate::AddressSpace::TaskPayload {
1509 if ep.task_payload != Some(var_handle) {
1510 return Err(EntryPointError::WrongTaskPayloadUsed
1511 .with_span_handle(var_handle, &module.global_variables));
1512 }
1513 let size = module.types[var.ty].inner.size(module.to_ctx());
1514 if size < 4 {
1515 return Err(EntryPointError::TaskPayloadTooSmall(size)
1516 .with_span_handle(var_handle, &module.global_variables));
1517 }
1518 }
1519
1520 let allowed_usage = match var.space {
1521 crate::AddressSpace::Function => unreachable!(),
1522 crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
1523 crate::AddressSpace::Storage { access } => storage_usage(access),
1524 crate::AddressSpace::Handle => match module.types[var.ty].inner {
1525 crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
1526 crate::TypeInner::Image {
1527 class: crate::ImageClass::Storage { access, .. },
1528 ..
1529 } => storage_usage(access),
1530 _ => GlobalUse::READ | GlobalUse::QUERY,
1531 },
1532 crate::TypeInner::Image {
1533 class: crate::ImageClass::Storage { access, .. },
1534 ..
1535 } => storage_usage(access),
1536 _ => GlobalUse::READ | GlobalUse::QUERY,
1537 },
1538 crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {
1539 GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY
1540 }
1541 crate::AddressSpace::TaskPayload => {
1542 GlobalUse::READ
1543 | GlobalUse::QUERY
1544 | if ep.stage == crate::ShaderStage::Task {
1545 GlobalUse::WRITE
1546 } else {
1547 GlobalUse::empty()
1548 }
1549 }
1550 crate::AddressSpace::Immediate => GlobalUse::READ,
1551 crate::AddressSpace::RayPayload => {
1552 if !matches!(
1553 ep.stage,
1554 crate::ShaderStage::RayGeneration
1555 | crate::ShaderStage::ClosestHit
1556 | crate::ShaderStage::Miss
1557 ) {
1558 return Err(EntryPointError::RayPayloadInInvalidStage(ep.stage)
1559 .with_span_handle(var_handle, &module.global_variables));
1560 }
1561 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1562 }
1563 crate::AddressSpace::IncomingRayPayload => {
1564 if !matches!(
1565 ep.stage,
1566 crate::ShaderStage::AnyHit
1567 | crate::ShaderStage::ClosestHit
1568 | crate::ShaderStage::Miss
1569 ) {
1570 return Err(EntryPointError::IncomingRayPayloadInInvalidStage(ep.stage)
1571 .with_span_handle(var_handle, &module.global_variables));
1572 }
1573 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1574 }
1575 };
1576 if !allowed_usage.contains(usage) {
1577 log::warn!("\tUsage error for: {var:?}");
1578 log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}");
1579 return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
1580 .with_span_handle(var_handle, &module.global_variables));
1581 }
1582
1583 if let Some(ref bind) = var.binding {
1584 if !self.ep_resource_bindings.insert(*bind) {
1585 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1586 return Err(EntryPointError::BindingCollision(var_handle)
1587 .with_span_handle(var_handle, &module.global_variables));
1588 }
1589 }
1590 }
1591 }
1592
1593 if let &Some(ref mesh_info) = &ep.mesh_info {
1596 if module.global_variables[mesh_info.output_variable].space
1597 != crate::AddressSpace::WorkGroup
1598 {
1599 return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
1600 }
1601
1602 let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);
1603 if let Some(e) = implied.2 {
1604 return Err(e);
1605 }
1606
1607 if let Some(e) = mesh_info.max_vertices_override {
1608 if let crate::Expression::Override(o) = module.global_expressions[e] {
1609 if implied.1[0] != Some(o) {
1610 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1611 }
1612 }
1613 }
1614 if let Some(e) = mesh_info.max_primitives_override {
1615 if let crate::Expression::Override(o) = module.global_expressions[e] {
1616 if implied.1[1] != Some(o) {
1617 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1618 }
1619 }
1620 }
1621
1622 implied.0.max_vertices_override = mesh_info.max_vertices_override;
1623 implied.0.max_primitives_override = mesh_info.max_primitives_override;
1624 if implied.0 != *mesh_info {
1625 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1626 }
1627 if mesh_info.topology == crate::MeshOutputTopology::Points
1628 && !self
1629 .capabilities
1630 .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)
1631 {
1632 return Err(EntryPointError::UnsupportedCapability(
1633 Capabilities::MESH_SHADER_POINT_TOPOLOGY,
1634 )
1635 .with_span());
1636 }
1637
1638 self.validate_mesh_output_type(
1639 ep,
1640 module,
1641 mesh_info.vertex_output_type,
1642 MeshOutputType::VertexOutput,
1643 )?;
1644 self.validate_mesh_output_type(
1645 ep,
1646 module,
1647 mesh_info.primitive_output_type,
1648 MeshOutputType::PrimitiveOutput,
1649 )?;
1650 }
1651
1652 Ok(info)
1653 }
1654}