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 #[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("Location {location} interpolation of an integer has to be flat")]
154 InvalidIntegerInterpolation { location: u32 },
155 #[error(transparent)]
156 Function(#[from] FunctionError),
157 #[error("Capability {0:?} is not supported")]
158 UnsupportedCapability(Capabilities),
159
160 #[error("mesh shader entry point missing mesh shader attributes")]
161 ExpectedMeshShaderAttributes,
162 #[error("Non mesh shader entry point cannot have mesh shader attributes")]
163 UnexpectedMeshShaderAttributes,
164 #[error("Non mesh/task shader entry point cannot have task payload attribute")]
165 UnexpectedTaskPayload,
166 #[error("Task payload must be declared with `var<task_payload>`")]
167 TaskPayloadWrongAddressSpace,
168 #[error("For a task payload to be used, it must be declared with @payload")]
169 WrongTaskPayloadUsed,
170 #[error("Task shader entry point must return @builtin(mesh_task_size) vec3<u32>")]
171 WrongTaskShaderEntryResult,
172 #[error("Task shaders must declare a task payload output")]
173 ExpectedTaskPayload,
174 #[error(
175 "Mesh shader output variable must be a struct with fields that are all allowed builtins"
176 )]
177 BadMeshOutputVariableType,
178 #[error("Mesh shader output variable fields must have types that are in accordance with the mesh shader spec")]
179 BadMeshOutputVariableField,
180 #[error("Mesh shader entry point cannot have a return type")]
181 UnexpectedMeshShaderEntryResult,
182 #[error(
183 "Mesh output type must be a user-defined struct with fields in alignment with the mesh shader spec"
184 )]
185 InvalidMeshOutputType,
186 #[error("Mesh primitive outputs must have exactly one of `@builtin(triangle_indices)`, `@builtin(line_indices)`, or `@builtin(point_index)`")]
187 InvalidMeshPrimitiveOutputType,
188 #[error("Mesh output global variable must live in the workgroup address space")]
189 WrongMeshOutputAddressSpace,
190 #[error("Task payload must be at least 4 bytes, but is {0} bytes")]
191 TaskPayloadTooSmall(u32),
192 #[error("Only the `ray_generation`, `closest_hit`, and `any_hit` shader stages can access a global variable in the `ray_payload` address space")]
193 RayPayloadInInvalidStage(crate::ShaderStage),
194 #[error("Only the `closest_hit`, `any_hit`, and `miss` shader stages can access a global variable in the `incoming_ray_payload` address space")]
195 IncomingRayPayloadInInvalidStage(crate::ShaderStage),
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 required = match sampling {
791 Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
792 _ => Capabilities::empty(),
793 };
794 if !self.capabilities.contains(required) {
795 return Err(VaryingError::UnsupportedCapability(required));
796 }
797
798 if interpolation != Some(crate::Interpolation::PerVertex) {
799 match ty_inner.scalar_kind() {
800 Some(crate::ScalarKind::Float) => {
801 if needs_interpolation && interpolation.is_none() {
802 return Err(VaryingError::MissingInterpolation);
803 }
804 }
805 Some(_) => {
806 if needs_interpolation
807 && interpolation != Some(crate::Interpolation::Flat)
808 {
809 return Err(VaryingError::InvalidInterpolation);
810 }
811 }
812 None => return Err(VaryingError::InvalidType(ty)),
813 }
814 }
815 }
816 }
817
818 Ok(())
819 }
820
821 fn validate(
822 &mut self,
823 ep: &crate::EntryPoint,
824 ty: Handle<crate::Type>,
825 binding: Option<&crate::Binding>,
826 ) -> Result<(), WithSpan<VaryingError>> {
827 let span_context = self.types.get_span_context(ty);
828 match binding {
829 Some(binding) => self
830 .validate_impl(ep, ty, binding)
831 .map_err(|e| e.with_span_context(span_context)),
832 None => {
833 let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {
834 if self.flags.contains(super::ValidationFlags::BINDINGS) {
835 return Err(VaryingError::MissingBinding.with_span());
836 } else {
837 return Ok(());
838 }
839 };
840
841 if self.type_info[ty.index()]
842 .flags
843 .contains(super::TypeFlags::IO_SHAREABLE)
844 {
845 if self.stage != crate::ShaderStage::Fragment {
849 return Err(
850 VaryingError::InvalidAttributeInStage("blend_src", self.stage)
851 .with_span(),
852 );
853 }
854 if !self.output {
855 return Err(VaryingError::InvalidInputAttributeInStage(
856 "blend_src",
857 self.stage,
858 )
859 .with_span());
860 }
861 if !self.location_mask.insert(0)
863 && self.flags.contains(super::ValidationFlags::BINDINGS)
864 {
865 return Err(VaryingError::BindingCollision { location: 0 }.with_span());
866 }
867
868 **self
869 .dual_source_blending
870 .as_mut()
871 .expect("unexpected dual source blending") = true;
872 } else {
873 for (index, member) in members.iter().enumerate() {
874 let span_context = self.types.get_span_context(ty);
875 match member.binding {
876 None => {
877 if self.flags.contains(super::ValidationFlags::BINDINGS) {
878 return Err(VaryingError::MemberMissingBinding(index as u32)
879 .with_span_context(span_context));
880 }
881 }
882 Some(ref binding) => self
883 .validate_impl(ep, member.ty, binding)
884 .map_err(|e| e.with_span_context(span_context))?,
885 }
886 }
887 }
888 Ok(())
889 }
890 }
891 }
892}
893
894impl super::Validator {
895 pub(super) fn validate_global_var(
896 &self,
897 var: &crate::GlobalVariable,
898 gctx: crate::proc::GlobalCtx,
899 mod_info: &ModuleInfo,
900 global_expr_kind: &crate::proc::ExpressionKindTracker,
901 ) -> Result<(), GlobalVariableError> {
902 use super::TypeFlags;
903
904 log::debug!("var {var:?}");
905 let inner_ty = match gctx.types[var.ty].inner {
906 crate::TypeInner::BindingArray { base, .. } => match var.space {
910 crate::AddressSpace::Storage { .. } => {
911 if !self
912 .capabilities
913 .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
914 {
915 return Err(GlobalVariableError::UnsupportedCapability(
916 Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
917 ));
918 }
919 base
920 }
921 crate::AddressSpace::Uniform => {
922 if !self
923 .capabilities
924 .contains(Capabilities::BUFFER_BINDING_ARRAY)
925 {
926 return Err(GlobalVariableError::UnsupportedCapability(
927 Capabilities::BUFFER_BINDING_ARRAY,
928 ));
929 }
930 base
931 }
932 crate::AddressSpace::Handle => {
933 match gctx.types[base].inner {
934 crate::TypeInner::Image { class, .. } => match class {
935 crate::ImageClass::Storage { .. } => {
936 if !self
937 .capabilities
938 .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
939 {
940 return Err(GlobalVariableError::UnsupportedCapability(
941 Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
942 ));
943 }
944 }
945 crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {
946 if !self
947 .capabilities
948 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
949 {
950 return Err(GlobalVariableError::UnsupportedCapability(
951 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
952 ));
953 }
954 }
955 crate::ImageClass::External => {
956 unreachable!("binding arrays of external images are not supported");
958 }
959 },
960 crate::TypeInner::Sampler { .. } => {
961 if !self
962 .capabilities
963 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
964 {
965 return Err(GlobalVariableError::UnsupportedCapability(
966 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
967 ));
968 }
969 }
970 crate::TypeInner::AccelerationStructure { .. } => {
971 if !self
972 .capabilities
973 .contains(Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY)
974 {
975 return Err(GlobalVariableError::UnsupportedCapability(
976 Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY,
977 ));
978 }
979 }
980 crate::TypeInner::RayQuery { .. } => {
981 unreachable!("binding arrays of ray queries are not supported");
983 }
984 _ => {
985 }
988 }
989 base
990 }
991 _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
992 },
993 _ => var.ty,
994 };
995 let type_info = &self.types[inner_ty.index()];
996
997 let (required_type_flags, is_resource) = match var.space {
998 crate::AddressSpace::Function => {
999 return Err(GlobalVariableError::InvalidUsage(var.space))
1000 }
1001 crate::AddressSpace::Storage { access } => {
1002 if let Err((ty_handle, disalignment)) = type_info.storage_layout {
1003 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1004 return Err(GlobalVariableError::Alignment(
1005 var.space,
1006 ty_handle,
1007 disalignment,
1008 ));
1009 }
1010 }
1011 if access == crate::StorageAccess::STORE {
1012 return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
1013 }
1014 (
1015 TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,
1016 true,
1017 )
1018 }
1019 crate::AddressSpace::Uniform => {
1020 if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
1021 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1022 return Err(GlobalVariableError::Alignment(
1023 var.space,
1024 ty_handle,
1025 disalignment,
1026 ));
1027 }
1028 }
1029 (
1030 TypeFlags::DATA
1031 | TypeFlags::COPY
1032 | TypeFlags::SIZED
1033 | TypeFlags::HOST_SHAREABLE
1034 | TypeFlags::CREATION_RESOLVED,
1035 true,
1036 )
1037 }
1038 crate::AddressSpace::Handle => {
1039 match gctx.types[inner_ty].inner {
1040 crate::TypeInner::Image { class, .. } => match class {
1041 crate::ImageClass::Storage {
1042 format:
1043 crate::StorageFormat::R16Unorm
1044 | crate::StorageFormat::R16Snorm
1045 | crate::StorageFormat::Rg16Unorm
1046 | crate::StorageFormat::Rg16Snorm
1047 | crate::StorageFormat::Rgba16Unorm
1048 | crate::StorageFormat::Rgba16Snorm,
1049 ..
1050 } => {
1051 if !self
1052 .capabilities
1053 .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)
1054 {
1055 return Err(GlobalVariableError::UnsupportedCapability(
1056 Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
1057 ));
1058 }
1059 }
1060 _ => {}
1061 },
1062 crate::TypeInner::Sampler { .. }
1063 | crate::TypeInner::AccelerationStructure { .. }
1064 | crate::TypeInner::RayQuery { .. } => {}
1065 _ => {
1066 return Err(GlobalVariableError::InvalidType(var.space));
1067 }
1068 }
1069
1070 (TypeFlags::empty(), true)
1071 }
1072 crate::AddressSpace::Private => (
1073 TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,
1074 false,
1075 ),
1076 crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
1077 crate::AddressSpace::TaskPayload => {
1078 if !self.capabilities.contains(Capabilities::MESH_SHADER) {
1079 return Err(GlobalVariableError::UnsupportedCapability(
1080 Capabilities::MESH_SHADER,
1081 ));
1082 }
1083 (TypeFlags::DATA | TypeFlags::SIZED, false)
1084 }
1085 crate::AddressSpace::Immediate => {
1086 if !self.capabilities.contains(Capabilities::IMMEDIATES) {
1087 return Err(GlobalVariableError::UnsupportedCapability(
1088 Capabilities::IMMEDIATES,
1089 ));
1090 }
1091 if let Err(ref err) = type_info.immediates_compatibility {
1092 return Err(GlobalVariableError::InvalidImmediateType(err.clone()));
1093 }
1094 (
1095 TypeFlags::DATA
1096 | TypeFlags::COPY
1097 | TypeFlags::HOST_SHAREABLE
1098 | TypeFlags::SIZED,
1099 false,
1100 )
1101 }
1102 crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
1103 if !self
1104 .capabilities
1105 .contains(Capabilities::RAY_TRACING_PIPELINE)
1106 {
1107 return Err(GlobalVariableError::UnsupportedCapability(
1108 Capabilities::RAY_TRACING_PIPELINE,
1109 ));
1110 }
1111 (TypeFlags::DATA | TypeFlags::SIZED, false)
1112 }
1113 };
1114
1115 if !type_info.flags.contains(required_type_flags) {
1116 return Err(GlobalVariableError::MissingTypeFlags {
1117 seen: type_info.flags,
1118 required: required_type_flags,
1119 });
1120 }
1121
1122 if is_resource != var.binding.is_some() {
1123 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1124 return Err(GlobalVariableError::InvalidBinding);
1125 }
1126 }
1127
1128 if var.space == crate::AddressSpace::TaskPayload {
1129 let ty = &gctx.types[var.ty].inner;
1130 if ty.try_size(gctx) == Some(0) {
1132 return Err(GlobalVariableError::ZeroSizedTaskPayload);
1133 }
1134 }
1135
1136 if !var.memory_decorations.is_empty()
1137 && !matches!(var.space, crate::AddressSpace::Storage { .. })
1138 {
1139 return Err(GlobalVariableError::InvalidMemoryDecorationsAddressSpace);
1140 }
1141 if var
1142 .memory_decorations
1143 .contains(crate::MemoryDecorations::COHERENT)
1144 && !self
1145 .capabilities
1146 .contains(Capabilities::MEMORY_DECORATION_COHERENT)
1147 {
1148 return Err(GlobalVariableError::CoherentNotSupported);
1149 }
1150 if var
1151 .memory_decorations
1152 .contains(crate::MemoryDecorations::VOLATILE)
1153 && !self
1154 .capabilities
1155 .contains(Capabilities::MEMORY_DECORATION_VOLATILE)
1156 {
1157 return Err(GlobalVariableError::VolatileNotSupported);
1158 }
1159
1160 if let Some(init) = var.init {
1161 match var.space {
1162 crate::AddressSpace::Private | crate::AddressSpace::Function => {}
1163 _ => {
1164 return Err(GlobalVariableError::InitializerNotAllowed(var.space));
1165 }
1166 }
1167
1168 if !global_expr_kind.is_const_or_override(init) {
1169 return Err(GlobalVariableError::InitializerExprType);
1170 }
1171
1172 if !gctx.compare_types(
1173 &crate::proc::TypeResolution::Handle(var.ty),
1174 &mod_info[init],
1175 ) {
1176 return Err(GlobalVariableError::InitializerType);
1177 }
1178 }
1179
1180 Ok(())
1181 }
1182
1183 fn validate_mesh_output_type(
1185 &mut self,
1186 ep: &crate::EntryPoint,
1187 module: &crate::Module,
1188 ty: Handle<crate::Type>,
1189 mesh_output_type: MeshOutputType,
1190 ) -> Result<(), WithSpan<EntryPointError>> {
1191 if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {
1192 return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));
1193 }
1194 let mut result_built_ins = crate::FastHashSet::default();
1195 let mut ctx = VaryingContext {
1196 stage: ep.stage,
1197 output: true,
1198 types: &module.types,
1199 type_info: &self.types,
1200 location_mask: &mut self.location_mask,
1201 dual_source_blending: None,
1202 built_ins: &mut result_built_ins,
1203 capabilities: self.capabilities,
1204 flags: self.flags,
1205 mesh_output_type,
1206 has_task_payload: ep.task_payload.is_some(),
1207 };
1208 ctx.validate(ep, ty, None)
1209 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1210 if mesh_output_type == MeshOutputType::PrimitiveOutput {
1211 let mut num_indices_builtins = 0;
1212 if result_built_ins.contains(&crate::BuiltIn::PointIndex) {
1213 num_indices_builtins += 1;
1214 }
1215 if result_built_ins.contains(&crate::BuiltIn::LineIndices) {
1216 num_indices_builtins += 1;
1217 }
1218 if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {
1219 num_indices_builtins += 1;
1220 }
1221 if num_indices_builtins != 1 {
1222 return Err(EntryPointError::InvalidMeshPrimitiveOutputType
1223 .with_span_handle(ty, &module.types));
1224 }
1225 } else if mesh_output_type == MeshOutputType::VertexOutput
1226 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1227 {
1228 return Err(
1229 EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)
1230 );
1231 }
1232
1233 Ok(())
1234 }
1235
1236 pub(super) fn validate_entry_point(
1237 &mut self,
1238 ep: &crate::EntryPoint,
1239 module: &crate::Module,
1240 mod_info: &ModuleInfo,
1241 ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
1242 match ep.stage {
1243 crate::ShaderStage::Task | crate::ShaderStage::Mesh
1244 if !self.capabilities.contains(Capabilities::MESH_SHADER) =>
1245 {
1246 return Err(
1247 EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),
1248 );
1249 }
1250 crate::ShaderStage::RayGeneration
1251 | crate::ShaderStage::AnyHit
1252 | crate::ShaderStage::ClosestHit
1253 | crate::ShaderStage::Miss
1254 if !self
1255 .capabilities
1256 .contains(Capabilities::RAY_TRACING_PIPELINE) =>
1257 {
1258 return Err(EntryPointError::UnsupportedCapability(
1259 Capabilities::RAY_TRACING_PIPELINE,
1260 )
1261 .with_span());
1262 }
1263 _ => {}
1264 }
1265 if ep.early_depth_test.is_some() {
1266 let required = Capabilities::EARLY_DEPTH_TEST;
1267 if !self.capabilities.contains(required) {
1268 return Err(
1269 EntryPointError::Result(VaryingError::UnsupportedCapability(required))
1270 .with_span(),
1271 );
1272 }
1273
1274 if ep.stage != crate::ShaderStage::Fragment {
1275 return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
1276 }
1277 }
1278
1279 if ep.stage.compute_like() {
1280 if ep
1281 .workgroup_size
1282 .iter()
1283 .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
1284 {
1285 return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
1286 }
1287 } else if ep.workgroup_size != [0; 3] {
1288 return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
1289 }
1290
1291 match (ep.stage, &ep.mesh_info) {
1292 (crate::ShaderStage::Mesh, &None) => {
1293 return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());
1294 }
1295 (crate::ShaderStage::Mesh, &Some(..)) => {}
1296 (_, &Some(_)) => {
1297 return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());
1298 }
1299 (_, _) => {}
1300 }
1301
1302 let mut info = self
1303 .validate_function(&ep.function, module, mod_info, true)
1304 .map_err(WithSpan::into_other)?;
1305
1306 match ep.stage {
1308 crate::ShaderStage::Task => {
1310 let Some(handle) = ep.task_payload else {
1311 return Err(EntryPointError::ExpectedTaskPayload.with_span());
1312 };
1313 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1314 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1315 .with_span_handle(handle, &module.global_variables));
1316 }
1317 info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);
1318 }
1319
1320 crate::ShaderStage::Mesh => {
1322 if let Some(handle) = ep.task_payload {
1323 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1324 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1325 .with_span_handle(handle, &module.global_variables));
1326 }
1327 info.insert_global_use(GlobalUse::READ, handle);
1328 }
1329 if let Some(ref mesh_info) = ep.mesh_info {
1330 info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);
1331 }
1332 }
1333
1334 _ => {
1336 if let Some(handle) = ep.task_payload {
1337 return Err(EntryPointError::UnexpectedTaskPayload
1338 .with_span_handle(handle, &module.global_variables));
1339 }
1340 }
1341 }
1342
1343 {
1344 use super::ShaderStages;
1345
1346 let stage_bit = match ep.stage {
1347 crate::ShaderStage::Vertex => ShaderStages::VERTEX,
1348 crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
1349 crate::ShaderStage::Compute => ShaderStages::COMPUTE,
1350 crate::ShaderStage::Mesh => ShaderStages::MESH,
1351 crate::ShaderStage::Task => ShaderStages::TASK,
1352 crate::ShaderStage::RayGeneration => ShaderStages::RAY_GENERATION,
1353 crate::ShaderStage::AnyHit => ShaderStages::ANY_HIT,
1354 crate::ShaderStage::ClosestHit => ShaderStages::CLOSEST_HIT,
1355 crate::ShaderStage::Miss => ShaderStages::MISS,
1356 };
1357
1358 if !info.available_stages.contains(stage_bit) {
1359 return Err(EntryPointError::ForbiddenStageOperations.with_span());
1360 }
1361 }
1362
1363 self.location_mask.make_empty();
1364 let mut argument_built_ins = crate::FastHashSet::default();
1365 for (index, fa) in ep.function.arguments.iter().enumerate() {
1367 let mut ctx = VaryingContext {
1368 stage: ep.stage,
1369 output: false,
1370 types: &module.types,
1371 type_info: &self.types,
1372 location_mask: &mut self.location_mask,
1373 dual_source_blending: Some(&mut info.dual_source_blending),
1374 built_ins: &mut argument_built_ins,
1375 capabilities: self.capabilities,
1376 flags: self.flags,
1377 mesh_output_type: MeshOutputType::None,
1378 has_task_payload: ep.task_payload.is_some(),
1379 };
1380 ctx.validate(ep, fa.ty, fa.binding.as_ref())
1381 .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
1382 }
1383
1384 self.location_mask.make_empty();
1385 if let Some(ref fr) = ep.function.result {
1386 let mut result_built_ins = crate::FastHashSet::default();
1387 let mut ctx = VaryingContext {
1388 stage: ep.stage,
1389 output: true,
1390 types: &module.types,
1391 type_info: &self.types,
1392 location_mask: &mut self.location_mask,
1393 dual_source_blending: Some(&mut info.dual_source_blending),
1394 built_ins: &mut result_built_ins,
1395 capabilities: self.capabilities,
1396 flags: self.flags,
1397 mesh_output_type: MeshOutputType::None,
1398 has_task_payload: ep.task_payload.is_some(),
1399 };
1400 ctx.validate(ep, fr.ty, fr.binding.as_ref())
1401 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1402 if ep.stage == crate::ShaderStage::Vertex
1403 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1404 {
1405 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1406 }
1407 if ep.stage == crate::ShaderStage::Mesh {
1408 return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span());
1409 }
1410 if ep.stage == crate::ShaderStage::Task {
1412 let ok = module.types[fr.ty].inner
1413 == crate::TypeInner::Vector {
1414 size: crate::VectorSize::Tri,
1415 scalar: crate::Scalar::U32,
1416 };
1417 if !ok {
1418 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1419 }
1420 }
1421 } else if ep.stage == crate::ShaderStage::Vertex {
1422 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1423 } else if ep.stage == crate::ShaderStage::Task {
1424 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1425 }
1426
1427 {
1428 let mut used_immediates = module
1429 .global_variables
1430 .iter()
1431 .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
1432 .map(|(handle, _)| handle)
1433 .filter(|&handle| !info[handle].is_empty());
1434 if let Some(handle) = used_immediates.nth(1) {
1437 return Err(EntryPointError::MoreThanOneImmediateUsed
1438 .with_span_handle(handle, &module.global_variables));
1439 }
1440 }
1441
1442 self.ep_resource_bindings.clear();
1443 for (var_handle, var) in module.global_variables.iter() {
1444 let usage = info[var_handle];
1445 if usage.is_empty() {
1446 continue;
1447 }
1448
1449 if var.space == crate::AddressSpace::TaskPayload {
1450 if ep.task_payload != Some(var_handle) {
1451 return Err(EntryPointError::WrongTaskPayloadUsed
1452 .with_span_handle(var_handle, &module.global_variables));
1453 }
1454 let size = module.types[var.ty].inner.size(module.to_ctx());
1455 if size < 4 {
1456 return Err(EntryPointError::TaskPayloadTooSmall(size)
1457 .with_span_handle(var_handle, &module.global_variables));
1458 }
1459 }
1460
1461 let allowed_usage = match var.space {
1462 crate::AddressSpace::Function => unreachable!(),
1463 crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
1464 crate::AddressSpace::Storage { access } => storage_usage(access),
1465 crate::AddressSpace::Handle => match module.types[var.ty].inner {
1466 crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
1467 crate::TypeInner::Image {
1468 class: crate::ImageClass::Storage { access, .. },
1469 ..
1470 } => storage_usage(access),
1471 _ => GlobalUse::READ | GlobalUse::QUERY,
1472 },
1473 crate::TypeInner::Image {
1474 class: crate::ImageClass::Storage { access, .. },
1475 ..
1476 } => storage_usage(access),
1477 _ => GlobalUse::READ | GlobalUse::QUERY,
1478 },
1479 crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {
1480 GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY
1481 }
1482 crate::AddressSpace::TaskPayload => {
1483 GlobalUse::READ
1484 | GlobalUse::QUERY
1485 | if ep.stage == crate::ShaderStage::Task {
1486 GlobalUse::WRITE
1487 } else {
1488 GlobalUse::empty()
1489 }
1490 }
1491 crate::AddressSpace::Immediate => GlobalUse::READ,
1492 crate::AddressSpace::RayPayload => {
1493 if !matches!(
1494 ep.stage,
1495 crate::ShaderStage::RayGeneration
1496 | crate::ShaderStage::ClosestHit
1497 | crate::ShaderStage::Miss
1498 ) {
1499 return Err(EntryPointError::RayPayloadInInvalidStage(ep.stage)
1500 .with_span_handle(var_handle, &module.global_variables));
1501 }
1502 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1503 }
1504 crate::AddressSpace::IncomingRayPayload => {
1505 if !matches!(
1506 ep.stage,
1507 crate::ShaderStage::AnyHit
1508 | crate::ShaderStage::ClosestHit
1509 | crate::ShaderStage::Miss
1510 ) {
1511 return Err(EntryPointError::IncomingRayPayloadInInvalidStage(ep.stage)
1512 .with_span_handle(var_handle, &module.global_variables));
1513 }
1514 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1515 }
1516 };
1517 if !allowed_usage.contains(usage) {
1518 log::warn!("\tUsage error for: {var:?}");
1519 log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}");
1520 return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
1521 .with_span_handle(var_handle, &module.global_variables));
1522 }
1523
1524 if let Some(ref bind) = var.binding {
1525 if !self.ep_resource_bindings.insert(*bind) {
1526 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1527 return Err(EntryPointError::BindingCollision(var_handle)
1528 .with_span_handle(var_handle, &module.global_variables));
1529 }
1530 }
1531 }
1532 }
1533
1534 if let &Some(ref mesh_info) = &ep.mesh_info {
1537 if module.global_variables[mesh_info.output_variable].space
1538 != crate::AddressSpace::WorkGroup
1539 {
1540 return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
1541 }
1542
1543 let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);
1544 if let Some(e) = implied.2 {
1545 return Err(e);
1546 }
1547
1548 if let Some(e) = mesh_info.max_vertices_override {
1549 if let crate::Expression::Override(o) = module.global_expressions[e] {
1550 if implied.1[0] != Some(o) {
1551 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1552 }
1553 }
1554 }
1555 if let Some(e) = mesh_info.max_primitives_override {
1556 if let crate::Expression::Override(o) = module.global_expressions[e] {
1557 if implied.1[1] != Some(o) {
1558 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1559 }
1560 }
1561 }
1562
1563 implied.0.max_vertices_override = mesh_info.max_vertices_override;
1564 implied.0.max_primitives_override = mesh_info.max_primitives_override;
1565 if implied.0 != *mesh_info {
1566 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1567 }
1568 if mesh_info.topology == crate::MeshOutputTopology::Points
1569 && !self
1570 .capabilities
1571 .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)
1572 {
1573 return Err(EntryPointError::UnsupportedCapability(
1574 Capabilities::MESH_SHADER_POINT_TOPOLOGY,
1575 )
1576 .with_span());
1577 }
1578
1579 self.validate_mesh_output_type(
1580 ep,
1581 module,
1582 mesh_info.vertex_output_type,
1583 MeshOutputType::VertexOutput,
1584 )?;
1585 self.validate_mesh_output_type(
1586 ep,
1587 module,
1588 mesh_info.primitive_output_type,
1589 MeshOutputType::PrimitiveOutput,
1590 )?;
1591 }
1592
1593 Ok(info)
1594 }
1595}