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}
51
52#[derive(Clone, Debug, thiserror::Error)]
53#[cfg_attr(test, derive(PartialEq))]
54pub enum VaryingError {
55 #[error("The type {0:?} does not match the varying")]
56 InvalidType(Handle<crate::Type>),
57 #[error("The type {0:?} cannot be used for user-defined entry point inputs or outputs")]
58 NotIOShareableType(Handle<crate::Type>),
59 #[error("Interpolation is not valid")]
60 InvalidInterpolation,
61 #[error("Cannot combine {interpolation:?} interpolation with the {sampling:?} sample type")]
62 InvalidInterpolationSamplingCombination {
63 interpolation: crate::Interpolation,
64 sampling: crate::Sampling,
65 },
66 #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")]
67 MissingInterpolation,
68 #[error("Built-in {0:?} is not available at this stage")]
69 InvalidBuiltInStage(crate::BuiltIn),
70 #[error("Built-in type for {0:?} is invalid. Found {1:?}")]
71 InvalidBuiltInType(crate::BuiltIn, crate::TypeInner),
72 #[error("Entry point arguments and return values must all have bindings")]
73 MissingBinding,
74 #[error("Struct member {0} is missing a binding")]
75 MemberMissingBinding(u32),
76 #[error("Multiple bindings at location {location} are present")]
77 BindingCollision { location: u32 },
78 #[error("Multiple bindings use the same `blend_src` {blend_src}")]
79 BindingCollisionBlendSrc { blend_src: u32 },
80 #[error("Built-in {0:?} is present more than once")]
81 DuplicateBuiltIn(crate::BuiltIn),
82 #[error("Capability {0:?} is not supported")]
83 UnsupportedCapability(Capabilities),
84 #[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
85 InvalidInputAttributeInStage(&'static str, crate::ShaderStage),
86 #[error("The attribute {0:?} is not valid for stage {1:?}")]
87 InvalidAttributeInStage(&'static str, crate::ShaderStage),
88 #[error("The `blend_src` attribute can only be used on location 0, only indices 0 and 1 are valid. Location was {location}, index was {blend_src}.")]
89 InvalidBlendSrcIndex { location: u32, blend_src: u32 },
90 #[error("If `blend_src` is used, there must be exactly two outputs both with location 0, one with `blend_src(0)` and the other with `blend_src(1)`.")]
91 IncompleteBlendSrcUsage,
92 #[error("If `blend_src` is used, both outputs must have the same type. `blend_src(0)` has type {blend_src_0_type:?} and `blend_src(1)` has type {blend_src_1_type:?}.")]
93 BlendSrcOutputTypeMismatch {
94 blend_src_0_type: Handle<crate::Type>,
95 blend_src_1_type: Handle<crate::Type>,
96 },
97 #[error("Workgroup size is multi dimensional, `@builtin(subgroup_id)` and `@builtin(subgroup_invocation_id)` are not supported.")]
98 InvalidMultiDimensionalSubgroupBuiltIn,
99 #[error("The `@per_primitive` attribute can only be used in fragment shader inputs or mesh shader primitive outputs")]
100 InvalidPerPrimitive,
101 #[error("Non-builtin members of a mesh primitive output struct must be decorated with `@per_primitive`")]
102 MissingPerPrimitive,
103}
104
105#[derive(Clone, Debug, thiserror::Error)]
106#[cfg_attr(test, derive(PartialEq))]
107pub enum EntryPointError {
108 #[error("Multiple conflicting entry points")]
109 Conflict,
110 #[error("Vertex shaders must return a `@builtin(position)` output value")]
111 MissingVertexOutputPosition,
112 #[error("Early depth test is not applicable")]
113 UnexpectedEarlyDepthTest,
114 #[error("Workgroup size is not applicable")]
115 UnexpectedWorkgroupSize,
116 #[error("Workgroup size is out of range")]
117 OutOfRangeWorkgroupSize,
118 #[error("Uses operations forbidden at this stage")]
119 ForbiddenStageOperations,
120 #[error("Global variable {0:?} is used incorrectly as {1:?}")]
121 InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse),
122 #[error("More than 1 immediate data variable is used")]
123 MoreThanOneImmediateUsed,
124 #[error("Bindings for {0:?} conflict with other resource")]
125 BindingCollision(Handle<crate::GlobalVariable>),
126 #[error("Argument {0} varying error")]
127 Argument(u32, #[source] VaryingError),
128 #[error(transparent)]
129 Result(#[from] VaryingError),
130 #[error("Location {location} interpolation of an integer has to be flat")]
131 InvalidIntegerInterpolation { location: u32 },
132 #[error(transparent)]
133 Function(#[from] FunctionError),
134 #[error("Capability {0:?} is not supported")]
135 UnsupportedCapability(Capabilities),
136
137 #[error("mesh shader entry point missing mesh shader attributes")]
138 ExpectedMeshShaderAttributes,
139 #[error("Non mesh shader entry point cannot have mesh shader attributes")]
140 UnexpectedMeshShaderAttributes,
141 #[error("Non mesh/task shader entry point cannot have task payload attribute")]
142 UnexpectedTaskPayload,
143 #[error("Task payload must be declared with `var<task_payload>`")]
144 TaskPayloadWrongAddressSpace,
145 #[error("For a task payload to be used, it must be declared with @payload")]
146 WrongTaskPayloadUsed,
147 #[error("Task shader entry point must return @builtin(mesh_task_size) vec3<u32>")]
148 WrongTaskShaderEntryResult,
149 #[error("Task shaders must declare a task payload output")]
150 ExpectedTaskPayload,
151 #[error(
152 "Mesh shader output variable must be a struct with fields that are all allowed builtins"
153 )]
154 BadMeshOutputVariableType,
155 #[error("Mesh shader output variable fields must have types that are in accordance with the mesh shader spec")]
156 BadMeshOutputVariableField,
157 #[error("Mesh shader entry point cannot have a return type")]
158 UnexpectedMeshShaderEntryResult,
159 #[error(
160 "Mesh output type must be a user-defined struct with fields in alignment with the mesh shader spec"
161 )]
162 InvalidMeshOutputType,
163 #[error("Mesh primitive outputs must have exactly one of `@builtin(triangle_indices)`, `@builtin(line_indices)`, or `@builtin(point_index)`")]
164 InvalidMeshPrimitiveOutputType,
165 #[error("Mesh output global variable must live in the workgroup address space")]
166 WrongMeshOutputAddressSpace,
167 #[error("Task payload must be at least 4 bytes, but is {0} bytes")]
168 TaskPayloadTooSmall(u32),
169}
170
171fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
172 let mut storage_usage = GlobalUse::QUERY;
173 if access.contains(crate::StorageAccess::LOAD) {
174 storage_usage |= GlobalUse::READ;
175 }
176 if access.contains(crate::StorageAccess::STORE) {
177 storage_usage |= GlobalUse::WRITE;
178 }
179 if access.contains(crate::StorageAccess::ATOMIC) {
180 storage_usage |= GlobalUse::ATOMIC;
181 }
182 storage_usage
183}
184
185#[derive(Clone, Copy, Debug, PartialEq, Eq)]
186enum MeshOutputType {
187 None,
188 VertexOutput,
189 PrimitiveOutput,
190}
191
192struct VaryingContext<'a> {
193 stage: crate::ShaderStage,
194 output: bool,
195 types: &'a UniqueArena<crate::Type>,
196 type_info: &'a Vec<super::r#type::TypeInfo>,
197 location_mask: &'a mut BitSet,
198 blend_src_mask: &'a mut BitSet,
199 built_ins: &'a mut crate::FastHashSet<crate::BuiltIn>,
200 capabilities: Capabilities,
201 flags: super::ValidationFlags,
202 mesh_output_type: MeshOutputType,
203 has_task_payload: bool,
204}
205
206impl VaryingContext<'_> {
207 fn validate_impl(
208 &mut self,
209 ep: &crate::EntryPoint,
210 ty: Handle<crate::Type>,
211 binding: &crate::Binding,
212 ) -> Result<(), VaryingError> {
213 use crate::{BuiltIn as Bi, ShaderStage as St, TypeInner as Ti, VectorSize as Vs};
214
215 let ty_inner = &self.types[ty].inner;
216 match *binding {
217 crate::Binding::BuiltIn(built_in) => {
218 let canonical = if let crate::BuiltIn::Position { .. } = built_in {
221 crate::BuiltIn::Position { invariant: false }
222 } else {
223 built_in
224 };
225
226 if self.built_ins.contains(&canonical) {
227 return Err(VaryingError::DuplicateBuiltIn(built_in));
228 }
229 self.built_ins.insert(canonical);
230
231 let required = match built_in {
232 Bi::ClipDistance => Capabilities::CLIP_DISTANCE,
233 Bi::CullDistance => Capabilities::CULL_DISTANCE,
234 Bi::PrimitiveIndex => Capabilities::PRIMITIVE_INDEX,
235 Bi::Barycentric => Capabilities::SHADER_BARYCENTRICS,
236 Bi::ViewIndex => Capabilities::MULTIVIEW,
237 Bi::SampleIndex => Capabilities::MULTISAMPLED_SHADING,
238 Bi::NumSubgroups
239 | Bi::SubgroupId
240 | Bi::SubgroupSize
241 | Bi::SubgroupInvocationId => Capabilities::SUBGROUP,
242 _ => Capabilities::empty(),
243 };
244 if !self.capabilities.contains(required) {
245 return Err(VaryingError::UnsupportedCapability(required));
246 }
247
248 if matches!(
249 built_in,
250 crate::BuiltIn::SubgroupId | crate::BuiltIn::SubgroupInvocationId
251 ) && ep.workgroup_size[1..].iter().any(|&s| s > 1)
252 {
253 return Err(VaryingError::InvalidMultiDimensionalSubgroupBuiltIn);
254 }
255
256 let (visible, type_good) = match built_in {
257 Bi::BaseInstance | Bi::BaseVertex | Bi::InstanceIndex | Bi::VertexIndex => (
258 self.stage == St::Vertex && !self.output,
259 *ty_inner == Ti::Scalar(crate::Scalar::U32),
260 ),
261 Bi::DrawID => (
262 (self.stage == St::Vertex
264 || self.stage == St::Task
265 || (self.stage == St::Mesh && !self.has_task_payload))
266 && !self.output,
267 *ty_inner == Ti::Scalar(crate::Scalar::U32),
268 ),
269 Bi::ClipDistance | Bi::CullDistance => (
270 (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
271 match *ty_inner {
272 Ti::Array { base, size, .. } => {
273 self.types[base].inner == Ti::Scalar(crate::Scalar::F32)
274 && match size {
275 crate::ArraySize::Constant(non_zero) => non_zero.get() <= 8,
276 _ => false,
277 }
278 }
279 _ => false,
280 },
281 ),
282 Bi::PointSize => (
283 (self.stage == St::Vertex || self.stage == St::Mesh) && self.output,
284 *ty_inner == Ti::Scalar(crate::Scalar::F32),
285 ),
286 Bi::PointCoord => (
287 self.stage == St::Fragment && !self.output,
288 *ty_inner
289 == Ti::Vector {
290 size: Vs::Bi,
291 scalar: crate::Scalar::F32,
292 },
293 ),
294 Bi::Position { .. } => (
295 match self.stage {
296 St::Vertex | St::Mesh => self.output,
297 St::Fragment => !self.output,
298 St::Compute | St::Task => false,
299 },
300 *ty_inner
301 == Ti::Vector {
302 size: Vs::Quad,
303 scalar: crate::Scalar::F32,
304 },
305 ),
306 Bi::ViewIndex => (
307 match self.stage {
308 St::Vertex | St::Fragment | St::Task | St::Mesh => !self.output,
309 St::Compute => false,
310 },
311 *ty_inner == Ti::Scalar(crate::Scalar::U32),
312 ),
313 Bi::FragDepth => (
314 self.stage == St::Fragment && self.output,
315 *ty_inner == Ti::Scalar(crate::Scalar::F32),
316 ),
317 Bi::FrontFacing => (
318 self.stage == St::Fragment && !self.output,
319 *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
320 ),
321 Bi::PrimitiveIndex => (
322 (self.stage == St::Fragment && !self.output)
323 || (self.stage == St::Mesh
324 && self.output
325 && self.mesh_output_type == MeshOutputType::PrimitiveOutput),
326 *ty_inner == Ti::Scalar(crate::Scalar::U32),
327 ),
328 Bi::Barycentric => (
329 self.stage == St::Fragment && !self.output,
330 *ty_inner
331 == Ti::Vector {
332 size: Vs::Tri,
333 scalar: crate::Scalar::F32,
334 },
335 ),
336 Bi::SampleIndex => (
337 self.stage == St::Fragment && !self.output,
338 *ty_inner == Ti::Scalar(crate::Scalar::U32),
339 ),
340 Bi::SampleMask => (
341 self.stage == St::Fragment,
342 *ty_inner == Ti::Scalar(crate::Scalar::U32),
343 ),
344 Bi::LocalInvocationIndex => (
345 self.stage.compute_like() && !self.output,
346 *ty_inner == Ti::Scalar(crate::Scalar::U32),
347 ),
348 Bi::GlobalInvocationId
349 | Bi::LocalInvocationId
350 | Bi::WorkGroupId
351 | Bi::WorkGroupSize
352 | Bi::NumWorkGroups => (
353 self.stage.compute_like() && !self.output,
354 *ty_inner
355 == Ti::Vector {
356 size: Vs::Tri,
357 scalar: crate::Scalar::U32,
358 },
359 ),
360 Bi::NumSubgroups | Bi::SubgroupId => (
361 self.stage.compute_like() && !self.output,
362 *ty_inner == Ti::Scalar(crate::Scalar::U32),
363 ),
364 Bi::SubgroupSize | Bi::SubgroupInvocationId => (
365 match self.stage {
366 St::Compute | St::Fragment | St::Task | St::Mesh => !self.output,
367 St::Vertex => false,
368 },
369 *ty_inner == Ti::Scalar(crate::Scalar::U32),
370 ),
371 Bi::CullPrimitive => (
372 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
373 *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
374 ),
375 Bi::PointIndex => (
376 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
377 *ty_inner == Ti::Scalar(crate::Scalar::U32),
378 ),
379 Bi::LineIndices => (
380 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
381 *ty_inner
382 == Ti::Vector {
383 size: Vs::Bi,
384 scalar: crate::Scalar::U32,
385 },
386 ),
387 Bi::TriangleIndices => (
388 self.mesh_output_type == MeshOutputType::PrimitiveOutput,
389 *ty_inner
390 == Ti::Vector {
391 size: Vs::Tri,
392 scalar: crate::Scalar::U32,
393 },
394 ),
395 Bi::MeshTaskSize => (
396 self.stage == St::Task && self.output,
397 *ty_inner
398 == Ti::Vector {
399 size: Vs::Tri,
400 scalar: crate::Scalar::U32,
401 },
402 ),
403 Bi::VertexCount | Bi::PrimitiveCount | Bi::Vertices | Bi::Primitives => {
405 (false, true)
406 }
407 };
408 match built_in {
409 Bi::CullPrimitive
410 | Bi::PointIndex
411 | Bi::LineIndices
412 | Bi::TriangleIndices
413 | Bi::MeshTaskSize
414 | Bi::VertexCount
415 | Bi::PrimitiveCount
416 | Bi::Vertices
417 | Bi::Primitives => {
418 if !self.capabilities.contains(Capabilities::MESH_SHADER) {
419 return Err(VaryingError::UnsupportedCapability(
420 Capabilities::MESH_SHADER,
421 ));
422 }
423 }
424 _ => (),
425 }
426
427 if !visible {
428 return Err(VaryingError::InvalidBuiltInStage(built_in));
429 }
430 if !type_good {
431 return Err(VaryingError::InvalidBuiltInType(built_in, ty_inner.clone()));
432 }
433 }
434 crate::Binding::Location {
435 location,
436 interpolation,
437 sampling,
438 blend_src,
439 per_primitive,
440 } => {
441 if per_primitive && !self.capabilities.contains(Capabilities::MESH_SHADER) {
442 return Err(VaryingError::UnsupportedCapability(
443 Capabilities::MESH_SHADER,
444 ));
445 }
446 if !self.type_info[ty.index()]
448 .flags
449 .contains(super::TypeFlags::IO_SHAREABLE)
450 {
451 return Err(VaryingError::NotIOShareableType(ty));
452 }
453
454 if self.mesh_output_type == MeshOutputType::PrimitiveOutput {
456 if !per_primitive {
458 return Err(VaryingError::MissingPerPrimitive);
459 }
460 } else if self.stage == crate::ShaderStage::Fragment && !self.output {
461 } else if per_primitive {
466 return Err(VaryingError::InvalidPerPrimitive);
468 }
469
470 if let Some(blend_src) = blend_src {
471 if !self
474 .capabilities
475 .contains(Capabilities::DUAL_SOURCE_BLENDING)
476 {
477 return Err(VaryingError::UnsupportedCapability(
478 Capabilities::DUAL_SOURCE_BLENDING,
479 ));
480 }
481 if self.stage != crate::ShaderStage::Fragment {
482 return Err(VaryingError::InvalidAttributeInStage(
483 "blend_src",
484 self.stage,
485 ));
486 }
487 if !self.output {
488 return Err(VaryingError::InvalidInputAttributeInStage(
489 "blend_src",
490 self.stage,
491 ));
492 }
493 if (blend_src != 0 && blend_src != 1) || location != 0 {
494 return Err(VaryingError::InvalidBlendSrcIndex {
495 location,
496 blend_src,
497 });
498 }
499 if !self.blend_src_mask.insert(blend_src as usize) {
500 return Err(VaryingError::BindingCollisionBlendSrc { blend_src });
501 }
502 } else if !self.location_mask.insert(location as usize)
503 && self.flags.contains(super::ValidationFlags::BINDINGS)
504 {
505 return Err(VaryingError::BindingCollision { location });
506 }
507
508 if let Some(interpolation) = interpolation {
509 let invalid_sampling = match (interpolation, sampling) {
510 (_, None)
511 | (
512 crate::Interpolation::Perspective | crate::Interpolation::Linear,
513 Some(
514 crate::Sampling::Center
515 | crate::Sampling::Centroid
516 | crate::Sampling::Sample,
517 ),
518 )
519 | (
520 crate::Interpolation::Flat,
521 Some(crate::Sampling::First | crate::Sampling::Either),
522 ) => None,
523 (_, Some(invalid_sampling)) => Some(invalid_sampling),
524 };
525 if let Some(sampling) = invalid_sampling {
526 return Err(VaryingError::InvalidInterpolationSamplingCombination {
527 interpolation,
528 sampling,
529 });
530 }
531 }
532
533 let needs_interpolation = match self.stage {
534 crate::ShaderStage::Vertex => self.output,
535 crate::ShaderStage::Fragment => !self.output && !per_primitive,
536 crate::ShaderStage::Compute | crate::ShaderStage::Task => false,
537 crate::ShaderStage::Mesh => self.output,
538 };
539
540 let _ = sampling;
544
545 let required = match sampling {
546 Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
547 _ => Capabilities::empty(),
548 };
549 if !self.capabilities.contains(required) {
550 return Err(VaryingError::UnsupportedCapability(required));
551 }
552
553 match ty_inner.scalar_kind() {
554 Some(crate::ScalarKind::Float) => {
555 if needs_interpolation && interpolation.is_none() {
556 return Err(VaryingError::MissingInterpolation);
557 }
558 }
559 Some(_) => {
560 if needs_interpolation && interpolation != Some(crate::Interpolation::Flat)
561 {
562 return Err(VaryingError::InvalidInterpolation);
563 }
564 }
565 None => return Err(VaryingError::InvalidType(ty)),
566 }
567 }
568 }
569
570 Ok(())
571 }
572
573 fn validate(
574 &mut self,
575 ep: &crate::EntryPoint,
576 ty: Handle<crate::Type>,
577 binding: Option<&crate::Binding>,
578 ) -> Result<(), WithSpan<VaryingError>> {
579 let span_context = self.types.get_span_context(ty);
580 match binding {
581 Some(binding) => self
582 .validate_impl(ep, ty, binding)
583 .map_err(|e| e.with_span_context(span_context)),
584 None => {
585 let crate::TypeInner::Struct { ref members, .. } = self.types[ty].inner else {
586 if self.flags.contains(super::ValidationFlags::BINDINGS) {
587 return Err(VaryingError::MissingBinding.with_span());
588 } else {
589 return Ok(());
590 }
591 };
592
593 for (index, member) in members.iter().enumerate() {
594 let span_context = self.types.get_span_context(ty);
595 match member.binding {
596 None => {
597 if self.flags.contains(super::ValidationFlags::BINDINGS) {
598 return Err(VaryingError::MemberMissingBinding(index as u32)
599 .with_span_context(span_context));
600 }
601 }
602 Some(ref binding) => self
603 .validate_impl(ep, member.ty, binding)
604 .map_err(|e| e.with_span_context(span_context))?,
605 }
606 }
607
608 if !self.blend_src_mask.is_empty() {
609 let span_context = self.types.get_span_context(ty);
610
611 if members.len() != 2 || self.blend_src_mask.len() != 2 {
613 return Err(
614 VaryingError::IncompleteBlendSrcUsage.with_span_context(span_context)
615 );
616 }
617 if members[0].ty != members[1].ty {
619 return Err(VaryingError::BlendSrcOutputTypeMismatch {
620 blend_src_0_type: members[0].ty,
621 blend_src_1_type: members[1].ty,
622 }
623 .with_span_context(span_context));
624 }
625 }
626 Ok(())
627 }
628 }
629 }
630}
631
632impl super::Validator {
633 pub(super) fn validate_global_var(
634 &self,
635 var: &crate::GlobalVariable,
636 gctx: crate::proc::GlobalCtx,
637 mod_info: &ModuleInfo,
638 global_expr_kind: &crate::proc::ExpressionKindTracker,
639 ) -> Result<(), GlobalVariableError> {
640 use super::TypeFlags;
641
642 log::debug!("var {var:?}");
643 let inner_ty = match gctx.types[var.ty].inner {
644 crate::TypeInner::BindingArray { base, .. } => match var.space {
648 crate::AddressSpace::Storage { .. } => {
649 if !self
650 .capabilities
651 .contains(Capabilities::STORAGE_BUFFER_BINDING_ARRAY)
652 {
653 return Err(GlobalVariableError::UnsupportedCapability(
654 Capabilities::STORAGE_BUFFER_BINDING_ARRAY,
655 ));
656 }
657 base
658 }
659 crate::AddressSpace::Uniform => {
660 if !self
661 .capabilities
662 .contains(Capabilities::BUFFER_BINDING_ARRAY)
663 {
664 return Err(GlobalVariableError::UnsupportedCapability(
665 Capabilities::BUFFER_BINDING_ARRAY,
666 ));
667 }
668 base
669 }
670 crate::AddressSpace::Handle => {
671 match gctx.types[base].inner {
672 crate::TypeInner::Image { class, .. } => match class {
673 crate::ImageClass::Storage { .. } => {
674 if !self
675 .capabilities
676 .contains(Capabilities::STORAGE_TEXTURE_BINDING_ARRAY)
677 {
678 return Err(GlobalVariableError::UnsupportedCapability(
679 Capabilities::STORAGE_TEXTURE_BINDING_ARRAY,
680 ));
681 }
682 }
683 crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => {
684 if !self
685 .capabilities
686 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
687 {
688 return Err(GlobalVariableError::UnsupportedCapability(
689 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
690 ));
691 }
692 }
693 crate::ImageClass::External => {
694 unreachable!("binding arrays of external images are not supported");
696 }
697 },
698 crate::TypeInner::Sampler { .. } => {
699 if !self
700 .capabilities
701 .contains(Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY)
702 {
703 return Err(GlobalVariableError::UnsupportedCapability(
704 Capabilities::TEXTURE_AND_SAMPLER_BINDING_ARRAY,
705 ));
706 }
707 }
708 crate::TypeInner::AccelerationStructure { .. } => {
709 return Err(GlobalVariableError::InvalidBindingArray(base));
710 }
711 crate::TypeInner::RayQuery { .. } => {
712 unreachable!("binding arrays of ray queries are not supported");
714 }
715 _ => {
716 }
719 }
720 base
721 }
722 _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
723 },
724 _ => var.ty,
725 };
726 let type_info = &self.types[inner_ty.index()];
727
728 let (required_type_flags, is_resource) = match var.space {
729 crate::AddressSpace::Function => {
730 return Err(GlobalVariableError::InvalidUsage(var.space))
731 }
732 crate::AddressSpace::Storage { access } => {
733 if let Err((ty_handle, disalignment)) = type_info.storage_layout {
734 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
735 return Err(GlobalVariableError::Alignment(
736 var.space,
737 ty_handle,
738 disalignment,
739 ));
740 }
741 }
742 if access == crate::StorageAccess::STORE {
743 return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
744 }
745 (
746 TypeFlags::DATA | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED,
747 true,
748 )
749 }
750 crate::AddressSpace::Uniform => {
751 if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
752 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
753 return Err(GlobalVariableError::Alignment(
754 var.space,
755 ty_handle,
756 disalignment,
757 ));
758 }
759 }
760 (
761 TypeFlags::DATA
762 | TypeFlags::COPY
763 | TypeFlags::SIZED
764 | TypeFlags::HOST_SHAREABLE
765 | TypeFlags::CREATION_RESOLVED,
766 true,
767 )
768 }
769 crate::AddressSpace::Handle => {
770 match gctx.types[inner_ty].inner {
771 crate::TypeInner::Image { class, .. } => match class {
772 crate::ImageClass::Storage {
773 format:
774 crate::StorageFormat::R16Unorm
775 | crate::StorageFormat::R16Snorm
776 | crate::StorageFormat::Rg16Unorm
777 | crate::StorageFormat::Rg16Snorm
778 | crate::StorageFormat::Rgba16Unorm
779 | crate::StorageFormat::Rgba16Snorm,
780 ..
781 } => {
782 if !self
783 .capabilities
784 .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)
785 {
786 return Err(GlobalVariableError::UnsupportedCapability(
787 Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
788 ));
789 }
790 }
791 _ => {}
792 },
793 crate::TypeInner::Sampler { .. }
794 | crate::TypeInner::AccelerationStructure { .. }
795 | crate::TypeInner::RayQuery { .. } => {}
796 _ => {
797 return Err(GlobalVariableError::InvalidType(var.space));
798 }
799 }
800
801 (TypeFlags::empty(), true)
802 }
803 crate::AddressSpace::Private => (
804 TypeFlags::CONSTRUCTIBLE | TypeFlags::CREATION_RESOLVED,
805 false,
806 ),
807 crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
808 crate::AddressSpace::TaskPayload => {
809 if !self.capabilities.contains(Capabilities::MESH_SHADER) {
810 return Err(GlobalVariableError::UnsupportedCapability(
811 Capabilities::MESH_SHADER,
812 ));
813 }
814 (TypeFlags::DATA | TypeFlags::SIZED, false)
815 }
816 crate::AddressSpace::Immediate => {
817 if !self.capabilities.contains(Capabilities::IMMEDIATES) {
818 return Err(GlobalVariableError::UnsupportedCapability(
819 Capabilities::IMMEDIATES,
820 ));
821 }
822 if let Err(ref err) = type_info.immediates_compatibility {
823 return Err(GlobalVariableError::InvalidImmediateType(err.clone()));
824 }
825 (
826 TypeFlags::DATA
827 | TypeFlags::COPY
828 | TypeFlags::HOST_SHAREABLE
829 | TypeFlags::SIZED,
830 false,
831 )
832 }
833 };
834
835 if !type_info.flags.contains(required_type_flags) {
836 return Err(GlobalVariableError::MissingTypeFlags {
837 seen: type_info.flags,
838 required: required_type_flags,
839 });
840 }
841
842 if is_resource != var.binding.is_some() {
843 if self.flags.contains(super::ValidationFlags::BINDINGS) {
844 return Err(GlobalVariableError::InvalidBinding);
845 }
846 }
847
848 if var.space == crate::AddressSpace::TaskPayload {
849 let ty = &gctx.types[var.ty].inner;
850 if ty.try_size(gctx) == Some(0) {
852 return Err(GlobalVariableError::ZeroSizedTaskPayload);
853 }
854 }
855
856 if let Some(init) = var.init {
857 match var.space {
858 crate::AddressSpace::Private | crate::AddressSpace::Function => {}
859 _ => {
860 return Err(GlobalVariableError::InitializerNotAllowed(var.space));
861 }
862 }
863
864 if !global_expr_kind.is_const_or_override(init) {
865 return Err(GlobalVariableError::InitializerExprType);
866 }
867
868 if !gctx.compare_types(
869 &crate::proc::TypeResolution::Handle(var.ty),
870 &mod_info[init],
871 ) {
872 return Err(GlobalVariableError::InitializerType);
873 }
874 }
875
876 Ok(())
877 }
878
879 fn validate_mesh_output_type(
881 &mut self,
882 ep: &crate::EntryPoint,
883 module: &crate::Module,
884 ty: Handle<crate::Type>,
885 mesh_output_type: MeshOutputType,
886 ) -> Result<(), WithSpan<EntryPointError>> {
887 if !matches!(module.types[ty].inner, crate::TypeInner::Struct { .. }) {
888 return Err(EntryPointError::InvalidMeshOutputType.with_span_handle(ty, &module.types));
889 }
890 let mut result_built_ins = crate::FastHashSet::default();
891 let mut ctx = VaryingContext {
892 stage: ep.stage,
893 output: true,
894 types: &module.types,
895 type_info: &self.types,
896 location_mask: &mut self.location_mask,
897 blend_src_mask: &mut self.blend_src_mask,
898 built_ins: &mut result_built_ins,
899 capabilities: self.capabilities,
900 flags: self.flags,
901 mesh_output_type,
902 has_task_payload: ep.task_payload.is_some(),
903 };
904 ctx.validate(ep, ty, None)
905 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
906 if mesh_output_type == MeshOutputType::PrimitiveOutput {
907 let mut num_indices_builtins = 0;
908 if result_built_ins.contains(&crate::BuiltIn::PointIndex) {
909 num_indices_builtins += 1;
910 }
911 if result_built_ins.contains(&crate::BuiltIn::LineIndices) {
912 num_indices_builtins += 1;
913 }
914 if result_built_ins.contains(&crate::BuiltIn::TriangleIndices) {
915 num_indices_builtins += 1;
916 }
917 if num_indices_builtins != 1 {
918 return Err(EntryPointError::InvalidMeshPrimitiveOutputType
919 .with_span_handle(ty, &module.types));
920 }
921 } else if mesh_output_type == MeshOutputType::VertexOutput
922 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
923 {
924 return Err(
925 EntryPointError::MissingVertexOutputPosition.with_span_handle(ty, &module.types)
926 );
927 }
928
929 Ok(())
930 }
931
932 pub(super) fn validate_entry_point(
933 &mut self,
934 ep: &crate::EntryPoint,
935 module: &crate::Module,
936 mod_info: &ModuleInfo,
937 ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
938 if matches!(
939 ep.stage,
940 crate::ShaderStage::Task | crate::ShaderStage::Mesh
941 ) && !self.capabilities.contains(Capabilities::MESH_SHADER)
942 {
943 return Err(
944 EntryPointError::UnsupportedCapability(Capabilities::MESH_SHADER).with_span(),
945 );
946 }
947 if ep.early_depth_test.is_some() {
948 let required = Capabilities::EARLY_DEPTH_TEST;
949 if !self.capabilities.contains(required) {
950 return Err(
951 EntryPointError::Result(VaryingError::UnsupportedCapability(required))
952 .with_span(),
953 );
954 }
955
956 if ep.stage != crate::ShaderStage::Fragment {
957 return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
958 }
959 }
960
961 if ep.stage.compute_like() {
962 if ep
963 .workgroup_size
964 .iter()
965 .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
966 {
967 return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
968 }
969 } else if ep.workgroup_size != [0; 3] {
970 return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
971 }
972
973 match (ep.stage, &ep.mesh_info) {
974 (crate::ShaderStage::Mesh, &None) => {
975 return Err(EntryPointError::ExpectedMeshShaderAttributes.with_span());
976 }
977 (crate::ShaderStage::Mesh, &Some(..)) => {}
978 (_, &Some(_)) => {
979 return Err(EntryPointError::UnexpectedMeshShaderAttributes.with_span());
980 }
981 (_, _) => {}
982 }
983
984 let mut info = self
985 .validate_function(&ep.function, module, mod_info, true)
986 .map_err(WithSpan::into_other)?;
987
988 match ep.stage {
990 crate::ShaderStage::Task => {
992 let Some(handle) = ep.task_payload else {
993 return Err(EntryPointError::ExpectedTaskPayload.with_span());
994 };
995 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
996 return Err(EntryPointError::TaskPayloadWrongAddressSpace
997 .with_span_handle(handle, &module.global_variables));
998 }
999 info.insert_global_use(GlobalUse::READ | GlobalUse::WRITE, handle);
1000 }
1001
1002 crate::ShaderStage::Mesh => {
1004 if let Some(handle) = ep.task_payload {
1005 if module.global_variables[handle].space != crate::AddressSpace::TaskPayload {
1006 return Err(EntryPointError::TaskPayloadWrongAddressSpace
1007 .with_span_handle(handle, &module.global_variables));
1008 }
1009 info.insert_global_use(GlobalUse::READ, handle);
1010 }
1011 if let Some(ref mesh_info) = ep.mesh_info {
1012 info.insert_global_use(GlobalUse::READ, mesh_info.output_variable);
1013 }
1014 }
1015
1016 _ => {
1018 if let Some(handle) = ep.task_payload {
1019 return Err(EntryPointError::UnexpectedTaskPayload
1020 .with_span_handle(handle, &module.global_variables));
1021 }
1022 }
1023 }
1024
1025 {
1026 use super::ShaderStages;
1027
1028 let stage_bit = match ep.stage {
1029 crate::ShaderStage::Vertex => ShaderStages::VERTEX,
1030 crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
1031 crate::ShaderStage::Compute => ShaderStages::COMPUTE,
1032 crate::ShaderStage::Mesh => ShaderStages::MESH,
1033 crate::ShaderStage::Task => ShaderStages::TASK,
1034 };
1035
1036 if !info.available_stages.contains(stage_bit) {
1037 return Err(EntryPointError::ForbiddenStageOperations.with_span());
1038 }
1039 }
1040
1041 self.location_mask.clear();
1042 let mut argument_built_ins = crate::FastHashSet::default();
1043 for (index, fa) in ep.function.arguments.iter().enumerate() {
1045 let mut ctx = VaryingContext {
1046 stage: ep.stage,
1047 output: false,
1048 types: &module.types,
1049 type_info: &self.types,
1050 location_mask: &mut self.location_mask,
1051 blend_src_mask: &mut self.blend_src_mask,
1052 built_ins: &mut argument_built_ins,
1053 capabilities: self.capabilities,
1054 flags: self.flags,
1055 mesh_output_type: MeshOutputType::None,
1056 has_task_payload: ep.task_payload.is_some(),
1057 };
1058 ctx.validate(ep, fa.ty, fa.binding.as_ref())
1059 .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
1060 }
1061
1062 self.location_mask.clear();
1063 if let Some(ref fr) = ep.function.result {
1064 let mut result_built_ins = crate::FastHashSet::default();
1065 let mut ctx = VaryingContext {
1066 stage: ep.stage,
1067 output: true,
1068 types: &module.types,
1069 type_info: &self.types,
1070 location_mask: &mut self.location_mask,
1071 blend_src_mask: &mut self.blend_src_mask,
1072 built_ins: &mut result_built_ins,
1073 capabilities: self.capabilities,
1074 flags: self.flags,
1075 mesh_output_type: MeshOutputType::None,
1076 has_task_payload: ep.task_payload.is_some(),
1077 };
1078 ctx.validate(ep, fr.ty, fr.binding.as_ref())
1079 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
1080 if ep.stage == crate::ShaderStage::Vertex
1081 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
1082 {
1083 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1084 }
1085 if ep.stage == crate::ShaderStage::Mesh {
1086 return Err(EntryPointError::UnexpectedMeshShaderEntryResult.with_span());
1087 }
1088 if ep.stage == crate::ShaderStage::Task {
1090 let ok = result_built_ins.contains(&crate::BuiltIn::MeshTaskSize)
1091 && result_built_ins.len() == 1
1092 && self.location_mask.is_empty();
1093 if !ok {
1094 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1095 }
1096 }
1097 if !self.blend_src_mask.is_empty() {
1098 info.dual_source_blending = true;
1099 }
1100 } else if ep.stage == crate::ShaderStage::Vertex {
1101 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
1102 } else if ep.stage == crate::ShaderStage::Task {
1103 return Err(EntryPointError::WrongTaskShaderEntryResult.with_span());
1104 }
1105
1106 {
1107 let mut used_immediates = module
1108 .global_variables
1109 .iter()
1110 .filter(|&(_, var)| var.space == crate::AddressSpace::Immediate)
1111 .map(|(handle, _)| handle)
1112 .filter(|&handle| !info[handle].is_empty());
1113 if let Some(handle) = used_immediates.nth(1) {
1116 return Err(EntryPointError::MoreThanOneImmediateUsed
1117 .with_span_handle(handle, &module.global_variables));
1118 }
1119 }
1120
1121 self.ep_resource_bindings.clear();
1122 for (var_handle, var) in module.global_variables.iter() {
1123 let usage = info[var_handle];
1124 if usage.is_empty() {
1125 continue;
1126 }
1127
1128 if var.space == crate::AddressSpace::TaskPayload {
1129 if ep.task_payload != Some(var_handle) {
1130 return Err(EntryPointError::WrongTaskPayloadUsed
1131 .with_span_handle(var_handle, &module.global_variables));
1132 }
1133 let size = module.types[var.ty].inner.size(module.to_ctx());
1134 if size < 4 {
1135 return Err(EntryPointError::TaskPayloadTooSmall(size)
1136 .with_span_handle(var_handle, &module.global_variables));
1137 }
1138 }
1139
1140 let allowed_usage = match var.space {
1141 crate::AddressSpace::Function => unreachable!(),
1142 crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
1143 crate::AddressSpace::Storage { access } => storage_usage(access),
1144 crate::AddressSpace::Handle => match module.types[var.ty].inner {
1145 crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
1146 crate::TypeInner::Image {
1147 class: crate::ImageClass::Storage { access, .. },
1148 ..
1149 } => storage_usage(access),
1150 _ => GlobalUse::READ | GlobalUse::QUERY,
1151 },
1152 crate::TypeInner::Image {
1153 class: crate::ImageClass::Storage { access, .. },
1154 ..
1155 } => storage_usage(access),
1156 _ => GlobalUse::READ | GlobalUse::QUERY,
1157 },
1158 crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => {
1159 GlobalUse::READ | GlobalUse::WRITE | GlobalUse::QUERY
1160 }
1161 crate::AddressSpace::TaskPayload => {
1162 GlobalUse::READ
1163 | GlobalUse::QUERY
1164 | if ep.stage == crate::ShaderStage::Task {
1165 GlobalUse::WRITE
1166 } else {
1167 GlobalUse::empty()
1168 }
1169 }
1170 crate::AddressSpace::Immediate => GlobalUse::READ,
1171 };
1172 if !allowed_usage.contains(usage) {
1173 log::warn!("\tUsage error for: {var:?}");
1174 log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}");
1175 return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
1176 .with_span_handle(var_handle, &module.global_variables));
1177 }
1178
1179 if let Some(ref bind) = var.binding {
1180 if !self.ep_resource_bindings.insert(*bind) {
1181 if self.flags.contains(super::ValidationFlags::BINDINGS) {
1182 return Err(EntryPointError::BindingCollision(var_handle)
1183 .with_span_handle(var_handle, &module.global_variables));
1184 }
1185 }
1186 }
1187 }
1188
1189 if let &Some(ref mesh_info) = &ep.mesh_info {
1192 if module.global_variables[mesh_info.output_variable].space
1193 != crate::AddressSpace::WorkGroup
1194 {
1195 return Err(EntryPointError::WrongMeshOutputAddressSpace.with_span());
1196 }
1197
1198 let mut implied = module.analyze_mesh_shader_info(mesh_info.output_variable);
1199 if let Some(e) = implied.2 {
1200 return Err(e);
1201 }
1202
1203 if let Some(e) = mesh_info.max_vertices_override {
1204 if let crate::Expression::Override(o) = module.global_expressions[e] {
1205 if implied.1[0] != Some(o) {
1206 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1207 }
1208 }
1209 }
1210 if let Some(e) = mesh_info.max_primitives_override {
1211 if let crate::Expression::Override(o) = module.global_expressions[e] {
1212 if implied.1[1] != Some(o) {
1213 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1214 }
1215 }
1216 }
1217
1218 implied.0.max_vertices_override = mesh_info.max_vertices_override;
1219 implied.0.max_primitives_override = mesh_info.max_primitives_override;
1220 if implied.0 != *mesh_info {
1221 return Err(EntryPointError::BadMeshOutputVariableType.with_span());
1222 }
1223 if mesh_info.topology == crate::MeshOutputTopology::Points
1224 && !self
1225 .capabilities
1226 .contains(Capabilities::MESH_SHADER_POINT_TOPOLOGY)
1227 {
1228 return Err(EntryPointError::UnsupportedCapability(
1229 Capabilities::MESH_SHADER_POINT_TOPOLOGY,
1230 )
1231 .with_span());
1232 }
1233
1234 self.validate_mesh_output_type(
1235 ep,
1236 module,
1237 mesh_info.vertex_output_type,
1238 MeshOutputType::VertexOutput,
1239 )?;
1240 self.validate_mesh_output_type(
1241 ep,
1242 module,
1243 mesh_info.primitive_output_type,
1244 MeshOutputType::PrimitiveOutput,
1245 )?;
1246 }
1247
1248 Ok(info)
1249 }
1250}