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