1use alloc::vec::Vec;
2
3use bit_set::BitSet;
4
5use super::{
6 analyzer::{FunctionInfo, GlobalUse},
7 Capabilities, Disalignment, FunctionError, ImmediateError, ModuleInfo,
8};
9use crate::arena::{Handle, UniqueArena};
10use crate::span::{AddSpan as _, MapErrWithSpan as _, SpanProvider as _, WithSpan};
11
12const MAX_WORKGROUP_SIZE: u32 = 0x4000;
13
14#[derive(Clone, Debug, thiserror::Error)]
15#[cfg_attr(test, derive(PartialEq))]
16pub enum GlobalVariableError {
17 #[error("Usage isn't compatible with address space {0:?}")]
18 InvalidUsage(crate::AddressSpace),
19 #[error("Type isn't compatible with address space {0:?}")]
20 InvalidType(crate::AddressSpace),
21 #[error("Type {0:?} isn't compatible with binding arrays")]
22 InvalidBindingArray(Handle<crate::Type>),
23 #[error("Type flags {seen:?} do not meet the required {required:?}")]
24 MissingTypeFlags {
25 required: super::TypeFlags,
26 seen: super::TypeFlags,
27 },
28 #[error("Capability {0:?} is not supported")]
29 UnsupportedCapability(Capabilities),
30 #[error("Binding decoration is missing or not applicable")]
31 InvalidBinding,
32 #[error("Alignment requirements for address space {0:?} are not met by {1:?}")]
33 Alignment(
34 crate::AddressSpace,
35 Handle<crate::Type>,
36 #[source] Disalignment,
37 ),
38 #[error("Initializer must be an override-expression")]
39 InitializerExprType,
40 #[error("Initializer doesn't match the variable type")]
41 InitializerType,
42 #[error("Initializer can't be used with address space {0:?}")]
43 InitializerNotAllowed(crate::AddressSpace),
44 #[error("Storage address space doesn't support write-only access")]
45 StorageAddressSpaceWriteOnlyNotSupported,
46 #[error("Type is not valid for use as a immediate data")]
47 InvalidImmediateType(#[source] ImmediateError),
48 #[error("Task payload must not be zero-sized")]
49 ZeroSizedTaskPayload,
50 #[error("Memory decorations (`@coherent`, `@volatile`) are only valid for variables in the `storage` address space")]
51 InvalidMemoryDecorationsAddressSpace,
52 #[error("`@coherent` requires the MEMORY_DECORATION_COHERENT capability")]
53 CoherentNotSupported,
54 #[error("`@volatile` requires the MEMORY_DECORATION_VOLATILE capability")]
55 VolatileNotSupported,
56}
57
58#[derive(Clone, Debug, thiserror::Error)]
59#[cfg_attr(test, derive(PartialEq))]
60pub enum VaryingError {
61 #[error("The type {0:?} does not match the varying")]
62 InvalidType(Handle<crate::Type>),
63 #[error(
64 "The type {0:?} cannot be used for user-defined entry point inputs or outputs. \
65 Only numeric scalars and vectors are allowed."
66 )]
67 NotIOShareableType(Handle<crate::Type>),
68 #[error("Interpolation {0:?} is only valid for stage {1:?}")]
69 InvalidInterpolationInStage(crate::Interpolation, crate::ShaderStage),
70 #[error("Cannot combine {interpolation:?} interpolation with the {sampling:?} sample type")]
71 InvalidInterpolationSamplingCombination {
72 interpolation: crate::Interpolation,
73 sampling: crate::Sampling,
74 },
75 #[error("`@interpolate(flat) must be explicitly specified for integer I/O")]
76 InvalidInterpolationForInteger,
77 #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")]
78 MissingInterpolation,
79 #[error("Built-in {0:?} is not available at this stage")]
80 InvalidBuiltInStage(crate::BuiltIn),
81 #[error("Built-in type for {0:?} is invalid. Found {1:?}")]
82 InvalidBuiltInType(crate::BuiltIn, crate::TypeInner),
83 #[error("Entry point arguments and return values must all have bindings")]
84 MissingBinding,
85 #[error("Struct member {0} is missing a binding")]
86 MemberMissingBinding(u32),
87 #[error("Multiple bindings at location {location} are present")]
88 BindingCollision { location: u32 },
89 #[error("Multiple bindings use the same `blend_src` {blend_src}")]
90 BindingCollisionBlendSrc { blend_src: u32 },
91 #[error("Built-in {0:?} is present more than once")]
92 DuplicateBuiltIn(crate::BuiltIn),
93 #[error("Capability {0:?} is not supported")]
94 UnsupportedCapability(Capabilities),
95 #[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
96 InvalidInputAttributeInStage(&'static str, crate::ShaderStage),
97 #[error("The attribute {0:?} is not valid for stage {1:?}")]
98 InvalidAttributeInStage(&'static str, crate::ShaderStage),
99 #[error("`@blend_src` can only be used at location 0, indices 0 and 1. Found `@location({location}) @blend_src({blend_src})`.")]
100 InvalidBlendSrcIndex { location: u32, blend_src: u32 },
101 #[error(
102 "`@blend_src` structure must specify two sources. \
103 Found `@blend_src({present_blend_src})` but not `@blend_src({absent_blend_src})`.",
104 absent_blend_src = if *present_blend_src == 0 { 1 } else { 0 },
105 )]
106 IncompleteBlendSrcUsage { present_blend_src: u32 },
107 #[error("Structure using `@blend_src` may not specify `@location` on any other members. Found a binding at `@location({location})`.")]
108 InvalidBlendSrcWithOtherBindings { location: u32 },
109 #[error("Both `@blend_src` structure members must have the same type. `blend_src(0)` has type {blend_src_0_type:?} and `blend_src(1)` has type {blend_src_1_type:?}.")]
110 BlendSrcOutputTypeMismatch {
111 blend_src_0_type: Handle<crate::Type>,
112 blend_src_1_type: Handle<crate::Type>,
113 },
114 #[error("`@blend_src` can only be used on struct members, not directly on entry point I/O")]
115 BlendSrcNotOnStructMember,
116 #[error("Workgroup size is multi dimensional, `@builtin(subgroup_id)` and `@builtin(subgroup_invocation_id)` are not supported.")]
117 InvalidMultiDimensionalSubgroupBuiltIn,
118 #[error("The `@per_primitive` attribute can only be used in fragment shader inputs or mesh shader primitive outputs")]
119 InvalidPerPrimitive,
120 #[error("Non-builtin members of a mesh primitive output struct must be decorated with `@per_primitive`")]
121 MissingPerPrimitive,
122 #[error("Per vertex fragment inputs must be an array of length 3.")]
123 PerVertexNotArrayOfThree,
124 #[error("Per vertex can only have Center sampling or no sampling modifier")]
125 InvalidPerVertexSampling,
126}
127
128#[derive(Clone, Debug, thiserror::Error)]
129#[cfg_attr(test, derive(PartialEq))]
130pub enum EntryPointError {
131 #[error("Multiple conflicting entry points")]
132 Conflict,
133 #[error("Vertex shaders must return a `@builtin(position)` output value")]
134 MissingVertexOutputPosition,
135 #[error("Early depth test is not applicable")]
136 UnexpectedEarlyDepthTest,
137 #[error("Workgroup size is not applicable")]
138 UnexpectedWorkgroupSize,
139 #[error("Workgroup size is out of range")]
140 OutOfRangeWorkgroupSize,
141 #[error("Uses operations forbidden at this stage")]
142 ForbiddenStageOperations,
143 #[error("Global variable {0:?} is used incorrectly as {1:?}")]
144 InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse),
145 #[error("More than 1 immediate data variable is used")]
146 MoreThanOneImmediateUsed,
147 #[error("Bindings for {0:?} conflict with other resource")]
148 BindingCollision(Handle<crate::GlobalVariable>),
149 #[error("Argument {0} varying error")]
150 Argument(u32, #[source] VaryingError),
151 #[error(transparent)]
152 Result(#[from] VaryingError),
153 #[error(transparent)]
154 Function(#[from] FunctionError),
155 #[error("Capability {0:?} is not supported")]
156 UnsupportedCapability(Capabilities),
157
158 #[error("mesh shader entry point missing mesh shader attributes")]
159 ExpectedMeshShaderAttributes,
160 #[error("Non mesh shader entry point cannot have mesh shader attributes")]
161 UnexpectedMeshShaderAttributes,
162 #[error("Non mesh/task shader entry point cannot have task payload attribute")]
163 UnexpectedTaskPayload,
164 #[error("Task payload must be declared with `var<task_payload>`")]
165 TaskPayloadWrongAddressSpace,
166 #[error("For a task payload to be used, it must be declared with @payload")]
167 WrongTaskPayloadUsed,
168 #[error("Task shader entry point must return @builtin(mesh_task_size) vec3<u32>")]
169 WrongTaskShaderEntryResult,
170 #[error("Task shaders must declare a task payload output")]
171 ExpectedTaskPayload,
172 #[error(
173 "Mesh shader output variable must be a struct with fields that are all allowed builtins"
174 )]
175 BadMeshOutputVariableType,
176 #[error("Mesh shader output variable fields must have types that are in accordance with the mesh shader spec")]
177 BadMeshOutputVariableField,
178 #[error("Mesh shader entry point cannot have a return type")]
179 UnexpectedMeshShaderEntryResult,
180 #[error(
181 "Mesh output type must be a user-defined struct with fields in alignment with the mesh shader spec"
182 )]
183 InvalidMeshOutputType,
184 #[error("Mesh primitive outputs must have exactly one of `@builtin(triangle_indices)`, `@builtin(line_indices)`, or `@builtin(point_index)`")]
185 InvalidMeshPrimitiveOutputType,
186 #[error("Mesh output global variable must live in the workgroup address space")]
187 WrongMeshOutputAddressSpace,
188 #[error("Task payload must be at least 4 bytes, but is {0} bytes")]
189 TaskPayloadTooSmall(u32),
190 #[error("Only the `ray_generation`, `closest_hit`, and `any_hit` shader stages can access a global variable in the `ray_payload` address space")]
191 RayPayloadInInvalidStage(crate::ShaderStage),
192 #[error("Only the `closest_hit`, `any_hit`, and `miss` shader stages can access a global variable in the `incoming_ray_payload` address space")]
193 IncomingRayPayloadInInvalidStage(crate::ShaderStage),
194}
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 if sampling.is_some_and(|e| e != crate::Sampling::Center) {
697 return Err(VaryingError::InvalidPerVertexSampling);
698 }
699 }
700 let (ty, ty_inner) = if interpolation == Some(crate::Interpolation::PerVertex) {
703 let three = crate::ArraySize::Constant(core::num::NonZeroU32::new(3).unwrap());
704 match ty_inner {
705 &Ti::Array { base, size, .. } if size == three => {
706 (base, &self.types[base].inner)
707 }
708 _ => return Err(VaryingError::PerVertexNotArrayOfThree),
709 }
710 } else {
711 (ty, ty_inner)
712 };
713
714 if !self.type_info[ty.index()]
716 .flags
717 .contains(super::TypeFlags::IO_SHAREABLE)
718 {
719 return Err(VaryingError::NotIOShareableType(ty));
720 }
721
722 if self.mesh_output_type == MeshOutputType::PrimitiveOutput {
724 if !per_primitive {
726 return Err(VaryingError::MissingPerPrimitive);
727 }
728 } else if self.stage == crate::ShaderStage::Fragment && !self.output {
729 } else if per_primitive {
734 return Err(VaryingError::InvalidPerPrimitive);
736 }
737
738 if blend_src.is_some() {
739 return Err(VaryingError::BlendSrcNotOnStructMember);
740 } else if !self.location_mask.insert(location as usize)
741 && self.flags.contains(super::ValidationFlags::BINDINGS)
742 {
743 return Err(VaryingError::BindingCollision { location });
744 }
745
746 if let Some(interpolation) = interpolation {
747 let invalid_sampling = match (interpolation, sampling) {
748 (_, None)
749 | (
750 crate::Interpolation::Perspective | crate::Interpolation::Linear,
751 Some(
752 crate::Sampling::Center
753 | crate::Sampling::Centroid
754 | crate::Sampling::Sample,
755 ),
756 )
757 | (
758 crate::Interpolation::Flat,
759 Some(crate::Sampling::First | crate::Sampling::Either),
760 ) => None,
761 (_, Some(invalid_sampling)) => Some(invalid_sampling),
762 };
763 if let Some(sampling) = invalid_sampling {
764 return Err(VaryingError::InvalidInterpolationSamplingCombination {
765 interpolation,
766 sampling,
767 });
768 }
769 }
770
771 let needs_interpolation = match self.stage {
772 crate::ShaderStage::Vertex => self.output,
773 crate::ShaderStage::Fragment => !self.output && !per_primitive,
774 crate::ShaderStage::Compute
775 | crate::ShaderStage::Task
776 | crate::ShaderStage::RayGeneration
777 | crate::ShaderStage::AnyHit
778 | crate::ShaderStage::ClosestHit
779 | crate::ShaderStage::Miss => false,
780 crate::ShaderStage::Mesh => self.output,
781 };
782
783 let _ = sampling;
787
788 let required = match sampling {
789 Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
790 _ => Capabilities::empty(),
791 };
792 if !self.capabilities.contains(required) {
793 return Err(VaryingError::UnsupportedCapability(required));
794 }
795
796 if interpolation != Some(crate::Interpolation::PerVertex) {
797 match ty_inner.scalar_kind() {
798 Some(crate::ScalarKind::Float) => {
799 if needs_interpolation && interpolation.is_none() {
801 return Err(VaryingError::MissingInterpolation);
802 }
803 }
804 Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) => {
805 if needs_interpolation
808 && interpolation != Some(crate::Interpolation::Flat)
809 {
810 return Err(VaryingError::InvalidInterpolationForInteger);
811 }
812 }
813 Some(_) | None => return Err(VaryingError::InvalidType(ty)),
814 }
815 }
816 }
817 }
818
819 Ok(())
820 }
821
822 fn validate(
823 &mut self,
824 ep: &crate::EntryPoint,
825 ty: Handle<crate::Type>,
826 binding: Option<&crate::Binding>,
827 ) -> Result<(), WithSpan<VaryingError>> {
828 let span_context = self.types.get_span_context(ty);
829 match binding {
830 Some(binding) => self
831 .validate_impl(ep, ty, binding)
832 .map_err(|e| e.with_span_context(span_context)),
833 None => {
834 let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {
835 if self.flags.contains(super::ValidationFlags::BINDINGS) {
836 return Err(VaryingError::MissingBinding.with_span());
837 } else {
838 return Ok(());
839 }
840 };
841
842 if self.type_info[ty.index()]
843 .flags
844 .contains(super::TypeFlags::IO_SHAREABLE)
845 {
846 if self.stage != crate::ShaderStage::Fragment {
850 return Err(
851 VaryingError::InvalidAttributeInStage("blend_src", self.stage)
852 .with_span(),
853 );
854 }
855 if !self.output {
856 return Err(VaryingError::InvalidInputAttributeInStage(
857 "blend_src",
858 self.stage,
859 )
860 .with_span());
861 }
862 if !self.location_mask.insert(0)
864 && self.flags.contains(super::ValidationFlags::BINDINGS)
865 {
866 return Err(VaryingError::BindingCollision { location: 0 }.with_span());
867 }
868
869 **self
870 .dual_source_blending
871 .as_mut()
872 .expect("unexpected dual source blending") = true;
873 } else {
874 for (index, member) in members.iter().enumerate() {
875 let span_context = self.types.get_span_context(ty);
876 match member.binding {
877 None => {
878 if self.flags.contains(super::ValidationFlags::BINDINGS) {
879 return Err(VaryingError::MemberMissingBinding(index as u32)
880 .with_span_context(span_context));
881 }
882 }
883 Some(ref binding) => self
884 .validate_impl(ep, member.ty, binding)
885 .map_err(|e| e.with_span_context(span_context))?,
886 }
887 }
888 }
889 Ok(())
890 }
891 }
892 }
893}
894
895impl super::Validator {
896 pub(super) fn validate_global_var(
897 &self,
898 var: &crate::GlobalVariable,
899 gctx: crate::proc::GlobalCtx,
900 mod_info: &ModuleInfo,
901 global_expr_kind: &crate::proc::ExpressionKindTracker,
902 ) -> Result<(), GlobalVariableError> {
903 use super::TypeFlags;
904
905 log::debug!("var {var:?}");
906 let inner_ty = match gctx.types[var.ty].inner {
907 crate::TypeInner::BindingArray { base, .. } => match var.space {
911 crate::AddressSpace::Storage { .. } => {
912 if !self
913 .capabilities
914 .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
915 {
916 return Err(GlobalVariableError::UnsupportedCapability(
917 Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
918 ));
919 }
920 base
921 }
922 crate::AddressSpace::Uniform => {
923 if !self
924 .capabilities
925 .contains(Capabilities::BUFFER_BINDING_ARRAY)
926 {
927 return Err(GlobalVariableError::UnsupportedCapability(
928 Capabilities::BUFFER_BINDING_ARRAY,
929 ));
930 }
931 base
932 }
933 crate::AddressSpace::Handle => {
934 match gctx.types[base].inner {
935 crate::TypeInner::Image { class, .. } => match class {
936 crate::ImageClass::Storage { .. } => {
937 if !self
938 .capabilities
939 .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
940 {
941 return Err(GlobalVariableError::UnsupportedCapability(
942 Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
943 ));
944 }
945 }
946 crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {
947 if !self
948 .capabilities
949 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
950 {
951 return Err(GlobalVariableError::UnsupportedCapability(
952 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
953 ));
954 }
955 }
956 crate::ImageClass::External => {
957 unreachable!("binding arrays of external images are not supported");
959 }
960 },
961 crate::TypeInner::Sampler { .. } => {
962 if !self
963 .capabilities
964 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
965 {
966 return Err(GlobalVariableError::UnsupportedCapability(
967 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
968 ));
969 }
970 }
971 crate::TypeInner::AccelerationStructure { .. } => {
972 if !self
973 .capabilities
974 .contains(Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY)
975 {
976 return Err(GlobalVariableError::UnsupportedCapability(
977 Capabilities::ACCELERATION_STRUCTURE_BINDING_ARRAY,
978 ));
979 }
980 }
981 crate::TypeInner::RayQuery { .. } => {
982 unreachable!("binding arrays of ray queries are not supported");
984 }
985 _ => {
986 }
989 }
990 base
991 }
992 _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
993 },
994 _ => var.ty,
995 };
996 let type_info = &self.types[inner_ty.index()];
997
998 let (required_type_flags, is_resource) = match var.space {
999 crate::AddressSpace::Function => {
1000 return Err(GlobalVariableError::InvalidUsage(var.space))
1001 }
1002 crate::AddressSpace::Storage { access } => {
1003 if let Err((ty_handle, disalignment)) = type_info.storage_layout {
1004 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1005 return Err(GlobalVariableError::Alignment(
1006 var.space,
1007 ty_handle,
1008 disalignment,
1009 ));
1010 }
1011 }
1012 if access == crate::StorageAccess::STORE {
1013 return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
1014 }
1015 (
1016 TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,
1017 true,
1018 )
1019 }
1020 crate::AddressSpace::Uniform => {
1021 if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
1022 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
1023 return Err(GlobalVariableError::Alignment(
1024 var.space,
1025 ty_handle,
1026 disalignment,
1027 ));
1028 }
1029 }
1030 (
1031 TypeFlags::DATA
1032 | TypeFlags::COPY
1033 | TypeFlags::SIZED
1034 | TypeFlags::HOST_SHAREABLE
1035 | TypeFlags::CREATION_RESOLVED,
1036 true,
1037 )
1038 }
1039 crate::AddressSpace::Handle => {
1040 match gctx.types[inner_ty].inner {
1041 crate::TypeInner::Image { class, .. } => match class {
1042 crate::ImageClass::Storage {
1043 format:
1044 crate::StorageFormat::R16Unorm
1045 | crate::StorageFormat::R16Snorm
1046 | crate::StorageFormat::Rg16Unorm
1047 | crate::StorageFormat::Rg16Snorm
1048 | crate::StorageFormat::Rgba16Unorm
1049 | crate::StorageFormat::Rgba16Snorm,
1050 ..
1051 } => {
1052 if !self
1053 .capabilities
1054 .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)
1055 {
1056 return Err(GlobalVariableError::UnsupportedCapability(
1057 Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
1058 ));
1059 }
1060 }
1061 _ => {}
1062 },
1063 crate::TypeInner::Sampler { .. }
1064 | crate::TypeInner::AccelerationStructure { .. }
1065 | crate::TypeInner::RayQuery { .. } => {}
1066 _ => {
1067 return Err(GlobalVariableError::InvalidType(var.space));
1068 }
1069 }
1070
1071 (TypeFlags::empty(), true)
1072 }
1073 crate::AddressSpace::Private => (
1074 TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,
1075 false,
1076 ),
1077 crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
1078 crate::AddressSpace::TaskPayload => {
1079 if !self.capabilities.contains(Capabilities::MESH_SHADER) {
1080 return Err(GlobalVariableError::UnsupportedCapability(
1081 Capabilities::MESH_SHADER,
1082 ));
1083 }
1084 (TypeFlags::DATA | TypeFlags::SIZED, false)
1085 }
1086 crate::AddressSpace::Immediate => {
1087 if !self.capabilities.contains(Capabilities::IMMEDIATES) {
1088 return Err(GlobalVariableError::UnsupportedCapability(
1089 Capabilities::IMMEDIATES,
1090 ));
1091 }
1092 if let Err(ref err) = type_info.immediates_compatibility {
1093 return Err(GlobalVariableError::InvalidImmediateType(err.clone()));
1094 }
1095 (
1096 TypeFlags::DATA
1097 | TypeFlags::COPY
1098 | TypeFlags::HOST_SHAREABLE
1099 | TypeFlags::SIZED,
1100 false,
1101 )
1102 }
1103 crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
1104 if !self
1105 .capabilities
1106 .contains(Capabilities::RAY_TRACING_PIPELINE)
1107 {
1108 return Err(GlobalVariableError::UnsupportedCapability(
1109 Capabilities::RAY_TRACING_PIPELINE,
1110 ));
1111 }
1112 (TypeFlags::DATA | TypeFlags::SIZED, false)
1113 }
1114 };
1115
1116 if !type_info.flags.contains(required_type_flags) {
1117 return Err(GlobalVariableError::MissingTypeFlags {
1118 seen: type_info.flags,
1119 required: required_type_flags,
1120 });
1121 }
1122
1123 if is_resource != var.binding.is_some() {
1124 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1125 return Err(GlobalVariableError::InvalidBinding);
1126 }
1127 }
1128
1129 if var.space == crate::AddressSpace::TaskPayload {
1130 let ty = &gctx.types[var.ty].inner;
1131 if ty.try_size(gctx) == Some(0) {
1133 return Err(GlobalVariableError::ZeroSizedTaskPayload);
1134 }
1135 }
1136
1137 if !var.memory_decorations.is_empty()
1138 && !matches!(var.space, crate::AddressSpace::Storage { .. })
1139 {
1140 return Err(GlobalVariableError::InvalidMemoryDecorationsAddressSpace);
1141 }
1142 if var
1143 .memory_decorations
1144 .contains(crate::MemoryDecorations::COHERENT)
1145 && !self
1146 .capabilities
1147 .contains(Capabilities::MEMORY_DECORATION_COHERENT)
1148 {
1149 return Err(GlobalVariableError::CoherentNotSupported);
1150 }
1151 if var
1152 .memory_decorations
1153 .contains(crate::MemoryDecorations::VOLATILE)
1154 && !self
1155 .capabilities
1156 .contains(Capabilities::MEMORY_DECORATION_VOLATILE)
1157 {
1158 return Err(GlobalVariableError::VolatileNotSupported);
1159 }
1160
1161 if let Some(init) = var.init {
1162 match var.space {
1163 crate::AddressSpace::Private | crate::AddressSpace::Function => {}
1164 _ => {
1165 return Err(GlobalVariableError::InitializerNotAllowed(var.space));
1166 }
1167 }
1168
1169 if !global_expr_kind.is_const_or_override(init) {
1170 return Err(GlobalVariableError::InitializerExprType);
1171 }
1172
1173 if !gctx.compare_types(
1174 &crate::proc::TypeResolution::Handle(var.ty),
1175 &mod_info[init],
1176 ) {
1177 return Err(GlobalVariableError::InitializerType);
1178 }
1179 }
1180
1181 Ok(())
1182 }
1183
1184 fn validate_mesh_output_type(
1186 &mut self,
1187 ep: &crate::EntryPoint,
1188 module: &crate::Module,
1189 ty: Handle<crate::Type>,
1190 mesh_output_type: MeshOutputType,
1191 ) -> Result<(), WithSpan<EntryPointError>> {
1192 if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {
1193 return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));
1194 }
1195 let mut result_built_ins = crate::FastHashSet::default();
1196 let mut ctx = VaryingContext {
1197 stage: ep.stage,
1198 output: true,
1199 types: &module.types,
1200 type_info: &self.types,
1201 location_mask: &mut self.location_mask,
1202 dual_source_blending: None,
1203 built_ins: &mut result_built_ins,
1204 capabilities: self.capabilities,
1205 flags: self.flags,
1206 mesh_output_type,
1207 has_task_payload: ep.task_payload.is_some(),
1208 };
1209 ctx.validate(ep, ty, None)
1210 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1211 if mesh_output_type == MeshOutputType::PrimitiveOutput {
1212 let mut num_indices_builtins = 0;
1213 if result_built_ins.contains(&crate::BuiltIn::PointIndex) {
1214 num_indices_builtins += 1;
1215 }
1216 if result_built_ins.contains(&crate::BuiltIn::LineIndices) {
1217 num_indices_builtins += 1;
1218 }
1219 if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {
1220 num_indices_builtins += 1;
1221 }
1222 if num_indices_builtins != 1 {
1223 return Err(EntryPointError::InvalidMeshPrimitiveOutputType
1224 .with_span_handle(ty, &module.types));
1225 }
1226 } else if mesh_output_type == MeshOutputType::VertexOutput
1227 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1228 {
1229 return Err(
1230 EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)
1231 );
1232 }
1233
1234 Ok(())
1235 }
1236
1237 pub(super) fn validate_entry_point(
1238 &mut self,
1239 ep: &crate::EntryPoint,
1240 module: &crate::Module,
1241 mod_info: &ModuleInfo,
1242 ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
1243 match ep.stage {
1244 crate::ShaderStage::Task | crate::ShaderStage::Mesh
1245 if !self.capabilities.contains(Capabilities::MESH_SHADER) =>
1246 {
1247 return Err(
1248 EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),
1249 );
1250 }
1251 crate::ShaderStage::RayGeneration
1252 | crate::ShaderStage::AnyHit
1253 | crate::ShaderStage::ClosestHit
1254 | crate::ShaderStage::Miss
1255 if !self
1256 .capabilities
1257 .contains(Capabilities::RAY_TRACING_PIPELINE) =>
1258 {
1259 return Err(EntryPointError::UnsupportedCapability(
1260 Capabilities::RAY_TRACING_PIPELINE,
1261 )
1262 .with_span());
1263 }
1264 _ => {}
1265 }
1266 if ep.early_depth_test.is_some() {
1267 let required = Capabilities::EARLY_DEPTH_TEST;
1268 if !self.capabilities.contains(required) {
1269 return Err(
1270 EntryPointError::Result(VaryingError::UnsupportedCapability(required))
1271 .with_span(),
1272 );
1273 }
1274
1275 if ep.stage != crate::ShaderStage::Fragment {
1276 return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
1277 }
1278 }
1279
1280 if ep.stage.compute_like() {
1281 if ep
1282 .workgroup_size
1283 .iter()
1284 .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
1285 {
1286 return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
1287 }
1288 } else if ep.workgroup_size != [0; 3] {
1289 return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
1290 }
1291
1292 match (ep.stage, &ep.mesh_info) {
1293 (crate::ShaderStage::Mesh, &None) => {
1294 return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());
1295 }
1296 (crate::ShaderStage::Mesh, &Some(..)) => {}
1297 (_, &Some(_)) => {
1298 return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());
1299 }
1300 (_, _) => {}
1301 }
1302
1303 let mut info = self
1304 .validate_function(&ep.function, module, mod_info, true)
1305 .map_err(WithSpan::into_other)?;
1306
1307 match ep.stage {
1309 crate::ShaderStage::Task => {
1311 let Some(handle) = ep.task_payload else {
1312 return Err(EntryPointError::ExpectedTaskPayload.with_span());
1313 };
1314 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1315 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1316 .with_span_handle(handle, &module.global_variables));
1317 }
1318 info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);
1319 }
1320
1321 crate::ShaderStage::Mesh => {
1323 if let Some(handle) = ep.task_payload {
1324 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1325 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1326 .with_span_handle(handle, &module.global_variables));
1327 }
1328 info.insert_global_use(GlobalUse::READ, handle);
1329 }
1330 if let Some(ref mesh_info) = ep.mesh_info {
1331 info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);
1332 }
1333 }
1334
1335 _ => {
1337 if let Some(handle) = ep.task_payload {
1338 return Err(EntryPointError::UnexpectedTaskPayload
1339 .with_span_handle(handle, &module.global_variables));
1340 }
1341 }
1342 }
1343
1344 {
1345 use super::ShaderStages;
1346
1347 let stage_bit = match ep.stage {
1348 crate::ShaderStage::Vertex => ShaderStages::VERTEX,
1349 crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
1350 crate::ShaderStage::Compute => ShaderStages::COMPUTE,
1351 crate::ShaderStage::Mesh => ShaderStages::MESH,
1352 crate::ShaderStage::Task => ShaderStages::TASK,
1353 crate::ShaderStage::RayGeneration => ShaderStages::RAY_GENERATION,
1354 crate::ShaderStage::AnyHit => ShaderStages::ANY_HIT,
1355 crate::ShaderStage::ClosestHit => ShaderStages::CLOSEST_HIT,
1356 crate::ShaderStage::Miss => ShaderStages::MISS,
1357 };
1358
1359 if !info.available_stages.contains(stage_bit) {
1360 return Err(EntryPointError::ForbiddenStageOperations.with_span());
1361 }
1362 }
1363
1364 self.location_mask.make_empty();
1365 let mut argument_built_ins = crate::FastHashSet::default();
1366 for (index, fa) in ep.function.arguments.iter().enumerate() {
1368 let mut ctx = VaryingContext {
1369 stage: ep.stage,
1370 output: false,
1371 types: &module.types,
1372 type_info: &self.types,
1373 location_mask: &mut self.location_mask,
1374 dual_source_blending: Some(&mut info.dual_source_blending),
1375 built_ins: &mut argument_built_ins,
1376 capabilities: self.capabilities,
1377 flags: self.flags,
1378 mesh_output_type: MeshOutputType::None,
1379 has_task_payload: ep.task_payload.is_some(),
1380 };
1381 ctx.validate(ep, fa.ty, fa.binding.as_ref())
1382 .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
1383 }
1384
1385 self.location_mask.make_empty();
1386 if let Some(ref fr) = ep.function.result {
1387 let mut result_built_ins = crate::FastHashSet::default();
1388 let mut ctx = VaryingContext {
1389 stage: ep.stage,
1390 output: true,
1391 types: &module.types,
1392 type_info: &self.types,
1393 location_mask: &mut self.location_mask,
1394 dual_source_blending: Some(&mut info.dual_source_blending),
1395 built_ins: &mut result_built_ins,
1396 capabilities: self.capabilities,
1397 flags: self.flags,
1398 mesh_output_type: MeshOutputType::None,
1399 has_task_payload: ep.task_payload.is_some(),
1400 };
1401 ctx.validate(ep, fr.ty, fr.binding.as_ref())
1402 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1403 if ep.stage == crate::ShaderStage::Vertex
1404 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1405 {
1406 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1407 }
1408 if ep.stage == crate::ShaderStage::Mesh {
1409 return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span());
1410 }
1411 if ep.stage == crate::ShaderStage::Task {
1413 let ok = module.types[fr.ty].inner
1414 == crate::TypeInner::Vector {
1415 size: crate::VectorSize::Tri,
1416 scalar: crate::Scalar::U32,
1417 };
1418 if !ok {
1419 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1420 }
1421 }
1422 } else if ep.stage == crate::ShaderStage::Vertex {
1423 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1424 } else if ep.stage == crate::ShaderStage::Task {
1425 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1426 }
1427
1428 {
1429 let mut used_immediates = module
1430 .global_variables
1431 .iter()
1432 .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
1433 .map(|(handle, _)| handle)
1434 .filter(|&handle| !info[handle].is_empty());
1435 if let Some(handle) = used_immediates.nth(1) {
1438 return Err(EntryPointError::MoreThanOneImmediateUsed
1439 .with_span_handle(handle, &module.global_variables));
1440 }
1441 }
1442
1443 self.ep_resource_bindings.clear();
1444 for (var_handle, var) in module.global_variables.iter() {
1445 let usage = info[var_handle];
1446 if usage.is_empty() {
1447 continue;
1448 }
1449
1450 if var.space == crate::AddressSpace::TaskPayload {
1451 if ep.task_payload != Some(var_handle) {
1452 return Err(EntryPointError::WrongTaskPayloadUsed
1453 .with_span_handle(var_handle, &module.global_variables));
1454 }
1455 let size = module.types[var.ty].inner.size(module.to_ctx());
1456 if size < 4 {
1457 return Err(EntryPointError::TaskPayloadTooSmall(size)
1458 .with_span_handle(var_handle, &module.global_variables));
1459 }
1460 }
1461
1462 let allowed_usage = match var.space {
1463 crate::AddressSpace::Function => unreachable!(),
1464 crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
1465 crate::AddressSpace::Storage { access } => storage_usage(access),
1466 crate::AddressSpace::Handle => match module.types[var.ty].inner {
1467 crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
1468 crate::TypeInner::Image {
1469 class: crate::ImageClass::Storage { access, .. },
1470 ..
1471 } => storage_usage(access),
1472 _ => GlobalUse::READ | GlobalUse::QUERY,
1473 },
1474 crate::TypeInner::Image {
1475 class: crate::ImageClass::Storage { access, .. },
1476 ..
1477 } => storage_usage(access),
1478 _ => GlobalUse::READ | GlobalUse::QUERY,
1479 },
1480 crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {
1481 GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY
1482 }
1483 crate::AddressSpace::TaskPayload => {
1484 GlobalUse::READ
1485 | GlobalUse::QUERY
1486 | if ep.stage == crate::ShaderStage::Task {
1487 GlobalUse::WRITE
1488 } else {
1489 GlobalUse::empty()
1490 }
1491 }
1492 crate::AddressSpace::Immediate => GlobalUse::READ,
1493 crate::AddressSpace::RayPayload => {
1494 if !matches!(
1495 ep.stage,
1496 crate::ShaderStage::RayGeneration
1497 | crate::ShaderStage::ClosestHit
1498 | crate::ShaderStage::Miss
1499 ) {
1500 return Err(EntryPointError::RayPayloadInInvalidStage(ep.stage)
1501 .with_span_handle(var_handle, &module.global_variables));
1502 }
1503 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1504 }
1505 crate::AddressSpace::IncomingRayPayload => {
1506 if !matches!(
1507 ep.stage,
1508 crate::ShaderStage::AnyHit
1509 | crate::ShaderStage::ClosestHit
1510 | crate::ShaderStage::Miss
1511 ) {
1512 return Err(EntryPointError::IncomingRayPayloadInInvalidStage(ep.stage)
1513 .with_span_handle(var_handle, &module.global_variables));
1514 }
1515 GlobalUse::READ | GlobalUse::QUERY | GlobalUse::WRITE
1516 }
1517 };
1518 if !allowed_usage.contains(usage) {
1519 log::warn!("\tUsage error for: {var:?}");
1520 log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}");
1521 return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
1522 .with_span_handle(var_handle, &module.global_variables));
1523 }
1524
1525 if let Some(ref bind) = var.binding {
1526 if !self.ep_resource_bindings.insert(*bind) {
1527 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1528 return Err(EntryPointError::BindingCollision(var_handle)
1529 .with_span_handle(var_handle, &module.global_variables));
1530 }
1531 }
1532 }
1533 }
1534
1535 if let &Some(ref mesh_info) = &ep.mesh_info {
1538 if module.global_variables[mesh_info.output_variable].space
1539 != crate::AddressSpace::WorkGroup
1540 {
1541 return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
1542 }
1543
1544 let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);
1545 if let Some(e) = implied.2 {
1546 return Err(e);
1547 }
1548
1549 if let Some(e) = mesh_info.max_vertices_override {
1550 if let crate::Expression::Override(o) = module.global_expressions[e] {
1551 if implied.1[0] != Some(o) {
1552 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1553 }
1554 }
1555 }
1556 if let Some(e) = mesh_info.max_primitives_override {
1557 if let crate::Expression::Override(o) = module.global_expressions[e] {
1558 if implied.1[1] != Some(o) {
1559 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1560 }
1561 }
1562 }
1563
1564 implied.0.max_vertices_override = mesh_info.max_vertices_override;
1565 implied.0.max_primitives_override = mesh_info.max_primitives_override;
1566 if implied.0 != *mesh_info {
1567 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1568 }
1569 if mesh_info.topology == crate::MeshOutputTopology::Points
1570 && !self
1571 .capabilities
1572 .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)
1573 {
1574 return Err(EntryPointError::UnsupportedCapability(
1575 Capabilities::MESH_SHADER_POINT_TOPOLOGY,
1576 )
1577 .with_span());
1578 }
1579
1580 self.validate_mesh_output_type(
1581 ep,
1582 module,
1583 mesh_info.vertex_output_type,
1584 MeshOutputType::VertexOutput,
1585 )?;
1586 self.validate_mesh_output_type(
1587 ep,
1588 module,
1589 mesh_info.primitive_output_type,
1590 MeshOutputType::PrimitiveOutput,
1591 )?;
1592 }
1593
1594 Ok(info)
1595 }
1596}