1use super::{
2 analyzer::{FunctionInfo, GlobalUse},
3 Capabilities, Disalignment, FunctionError, ModuleInfo,
4};
5use crate::arena::{Handle, UniqueArena};
6
7use crate::span::{AddSpan as _, MapErrWithSpan as _, SpanProvider as _, WithSpan};
8use bit_set::BitSet;
9
10const MAX_WORKGROUP_SIZE: u32 = 0x4000;
11
12#[derive(Clone, Debug, thiserror::Error)]
13#[cfg_attr(test, derive(PartialEq))]
14pub enum GlobalVariableError {
15 #[error("Usage isn't compatible with address space {0:?}")]
16 InvalidUsage(crate::AddressSpace),
17 #[error("Type isn't compatible with address space {0:?}")]
18 InvalidType(crate::AddressSpace),
19 #[error("Type flags {seen:?} do not meet the required {required:?}")]
20 MissingTypeFlags {
21 required: super::TypeFlags,
22 seen: super::TypeFlags,
23 },
24 #[error("Capability {0:?} is not supported")]
25 UnsupportedCapability(Capabilities),
26 #[error("Binding decoration is missing or not applicable")]
27 InvalidBinding,
28 #[error("Alignment requirements for address space {0:?} are not met by {1:?}")]
29 Alignment(
30 crate::AddressSpace,
31 Handle<crate::Type>,
32 #[source] Disalignment,
33 ),
34 #[error("Initializer must be an override-expression")]
35 InitializerExprType,
36 #[error("Initializer doesn't match the variable type")]
37 InitializerType,
38 #[error("Initializer can't be used with address space {0:?}")]
39 InitializerNotAllowed(crate::AddressSpace),
40 #[error("Storage address space doesn't support write-only access")]
41 StorageAddressSpaceWriteOnlyNotSupported,
42}
43
44#[derive(Clone, Debug, thiserror::Error)]
45#[cfg_attr(test, derive(PartialEq))]
46pub enum VaryingError {
47 #[error("The type {0:?} does not match the varying")]
48 InvalidType(Handle<crate::Type>),
49 #[error("The type {0:?} cannot be used for user-defined entry point inputs or outputs")]
50 NotIOShareableType(Handle<crate::Type>),
51 #[error("Interpolation is not valid")]
52 InvalidInterpolation,
53 #[error("Interpolation must be specified on vertex shader outputs and fragment shader inputs")]
54 MissingInterpolation,
55 #[error("Built-in {0:?} is not available at this stage")]
56 InvalidBuiltInStage(crate::BuiltIn),
57 #[error("Built-in type for {0:?} is invalid")]
58 InvalidBuiltInType(crate::BuiltIn),
59 #[error("Entry point arguments and return values must all have bindings")]
60 MissingBinding,
61 #[error("Struct member {0} is missing a binding")]
62 MemberMissingBinding(u32),
63 #[error("Multiple bindings at location {location} are present")]
64 BindingCollision { location: u32 },
65 #[error("Built-in {0:?} is present more than once")]
66 DuplicateBuiltIn(crate::BuiltIn),
67 #[error("Capability {0:?} is not supported")]
68 UnsupportedCapability(Capabilities),
69 #[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
70 InvalidInputAttributeInStage(&'static str, crate::ShaderStage),
71 #[error("The attribute {0:?} is not valid for stage {1:?}")]
72 InvalidAttributeInStage(&'static str, crate::ShaderStage),
73 #[error(
74 "The location index {location} cannot be used together with the attribute {attribute:?}"
75 )]
76 InvalidLocationAttributeCombination {
77 location: u32,
78 attribute: &'static str,
79 },
80 #[error("Workgroup size is multi dimensional, @builtin(subgroup_id) and @builtin(subgroup_invocation_id) are not supported.")]
81 InvalidMultiDimensionalSubgroupBuiltIn,
82}
83
84#[derive(Clone, Debug, thiserror::Error)]
85#[cfg_attr(test, derive(PartialEq))]
86pub enum EntryPointError {
87 #[error("Multiple conflicting entry points")]
88 Conflict,
89 #[error("Vertex shaders must return a `@builtin(position)` output value")]
90 MissingVertexOutputPosition,
91 #[error("Early depth test is not applicable")]
92 UnexpectedEarlyDepthTest,
93 #[error("Workgroup size is not applicable")]
94 UnexpectedWorkgroupSize,
95 #[error("Workgroup size is out of range")]
96 OutOfRangeWorkgroupSize,
97 #[error("Uses operations forbidden at this stage")]
98 ForbiddenStageOperations,
99 #[error("Global variable {0:?} is used incorrectly as {1:?}")]
100 InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse),
101 #[error("More than 1 push constant variable is used")]
102 MoreThanOnePushConstantUsed,
103 #[error("Bindings for {0:?} conflict with other resource")]
104 BindingCollision(Handle<crate::GlobalVariable>),
105 #[error("Argument {0} varying error")]
106 Argument(u32, #[source] VaryingError),
107 #[error(transparent)]
108 Result(#[from] VaryingError),
109 #[error("Location {location} interpolation of an integer has to be flat")]
110 InvalidIntegerInterpolation { location: u32 },
111 #[error(transparent)]
112 Function(#[from] FunctionError),
113 #[error(
114 "Invalid locations {location_mask:?} are set while dual source blending. Only location 0 may be set."
115 )]
116 InvalidLocationsWhileDualSourceBlending { location_mask: BitSet },
117}
118
119fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
120 let mut storage_usage = GlobalUse::QUERY;
121 if access.contains(crate::StorageAccess::LOAD) {
122 storage_usage |= GlobalUse::READ;
123 }
124 if access.contains(crate::StorageAccess::STORE) {
125 storage_usage |= GlobalUse::WRITE;
126 }
127 storage_usage
128}
129
130struct VaryingContext<'a> {
131 stage: crate::ShaderStage,
132 output: bool,
133 second_blend_source: bool,
134 types: &'a UniqueArena<crate::Type>,
135 type_info: &'a Vec<super::r#type::TypeInfo>,
136 location_mask: &'a mut BitSet,
137 built_ins: &'a mut crate::FastHashSet<crate::BuiltIn>,
138 capabilities: Capabilities,
139 flags: super::ValidationFlags,
140}
141
142impl VaryingContext<'_> {
143 fn validate_impl(
144 &mut self,
145 ep: &crate::EntryPoint,
146 ty: Handle<crate::Type>,
147 binding: &crate::Binding,
148 ) -> Result<(), VaryingError> {
149 use crate::{BuiltIn as Bi, ShaderStage as St, TypeInner as Ti, VectorSize as Vs};
150
151 let ty_inner = &self.types[ty].inner;
152 match *binding {
153 crate::Binding::BuiltIn(built_in) => {
154 let canonical = if let crate::BuiltIn::Position { .. } = built_in {
157 crate::BuiltIn::Position { invariant: false }
158 } else {
159 built_in
160 };
161
162 if self.built_ins.contains(&canonical) {
163 return Err(VaryingError::DuplicateBuiltIn(built_in));
164 }
165 self.built_ins.insert(canonical);
166
167 let required = match built_in {
168 Bi::ClipDistance => Capabilities::CLIP_DISTANCE,
169 Bi::CullDistance => Capabilities::CULL_DISTANCE,
170 Bi::PrimitiveIndex => Capabilities::PRIMITIVE_INDEX,
171 Bi::ViewIndex => Capabilities::MULTIVIEW,
172 Bi::SampleIndex => Capabilities::MULTISAMPLED_SHADING,
173 Bi::NumSubgroups
174 | Bi::SubgroupId
175 | Bi::SubgroupSize
176 | Bi::SubgroupInvocationId => Capabilities::SUBGROUP,
177 _ => Capabilities::empty(),
178 };
179 if !self.capabilities.contains(required) {
180 return Err(VaryingError::UnsupportedCapability(required));
181 }
182
183 if matches!(
184 built_in,
185 crate::BuiltIn::SubgroupId | crate::BuiltIn::SubgroupInvocationId
186 ) && ep.workgroup_size[1..].iter().any(|&s| s > 1)
187 {
188 return Err(VaryingError::InvalidMultiDimensionalSubgroupBuiltIn);
189 }
190
191 let (visible, type_good) = match built_in {
192 Bi::BaseInstance | Bi::BaseVertex | Bi::InstanceIndex | Bi::VertexIndex => (
193 self.stage == St::Vertex && !self.output,
194 *ty_inner == Ti::Scalar(crate::Scalar::U32),
195 ),
196 Bi::ClipDistance | Bi::CullDistance => (
197 self.stage == St::Vertex && self.output,
198 match *ty_inner {
199 Ti::Array { base, .. } => {
200 self.types[base].inner == Ti::Scalar(crate::Scalar::F32)
201 }
202 _ => false,
203 },
204 ),
205 Bi::PointSize => (
206 self.stage == St::Vertex && self.output,
207 *ty_inner == Ti::Scalar(crate::Scalar::F32),
208 ),
209 Bi::PointCoord => (
210 self.stage == St::Fragment && !self.output,
211 *ty_inner
212 == Ti::Vector {
213 size: Vs::Bi,
214 scalar: crate::Scalar::F32,
215 },
216 ),
217 Bi::Position { .. } => (
218 match self.stage {
219 St::Vertex => self.output,
220 St::Fragment => !self.output,
221 St::Compute => false,
222 },
223 *ty_inner
224 == Ti::Vector {
225 size: Vs::Quad,
226 scalar: crate::Scalar::F32,
227 },
228 ),
229 Bi::ViewIndex => (
230 match self.stage {
231 St::Vertex | St::Fragment => !self.output,
232 St::Compute => false,
233 },
234 *ty_inner == Ti::Scalar(crate::Scalar::I32),
235 ),
236 Bi::FragDepth => (
237 self.stage == St::Fragment && self.output,
238 *ty_inner == Ti::Scalar(crate::Scalar::F32),
239 ),
240 Bi::FrontFacing => (
241 self.stage == St::Fragment && !self.output,
242 *ty_inner == Ti::Scalar(crate::Scalar::BOOL),
243 ),
244 Bi::PrimitiveIndex => (
245 self.stage == St::Fragment && !self.output,
246 *ty_inner == Ti::Scalar(crate::Scalar::U32),
247 ),
248 Bi::SampleIndex => (
249 self.stage == St::Fragment && !self.output,
250 *ty_inner == Ti::Scalar(crate::Scalar::U32),
251 ),
252 Bi::SampleMask => (
253 self.stage == St::Fragment,
254 *ty_inner == Ti::Scalar(crate::Scalar::U32),
255 ),
256 Bi::LocalInvocationIndex => (
257 self.stage == St::Compute && !self.output,
258 *ty_inner == Ti::Scalar(crate::Scalar::U32),
259 ),
260 Bi::GlobalInvocationId
261 | Bi::LocalInvocationId
262 | Bi::WorkGroupId
263 | Bi::WorkGroupSize
264 | Bi::NumWorkGroups => (
265 self.stage == St::Compute && !self.output,
266 *ty_inner
267 == Ti::Vector {
268 size: Vs::Tri,
269 scalar: crate::Scalar::U32,
270 },
271 ),
272 Bi::NumSubgroups | Bi::SubgroupId => (
273 self.stage == St::Compute && !self.output,
274 *ty_inner == Ti::Scalar(crate::Scalar::U32),
275 ),
276 Bi::SubgroupSize | Bi::SubgroupInvocationId => (
277 match self.stage {
278 St::Compute | St::Fragment => !self.output,
279 St::Vertex => false,
280 },
281 *ty_inner == Ti::Scalar(crate::Scalar::U32),
282 ),
283 };
284
285 if !visible {
286 return Err(VaryingError::InvalidBuiltInStage(built_in));
287 }
288 if !type_good {
289 log::warn!("Wrong builtin type: {:?}", ty_inner);
290 return Err(VaryingError::InvalidBuiltInType(built_in));
291 }
292 }
293 crate::Binding::Location {
294 location,
295 interpolation,
296 sampling,
297 second_blend_source,
298 } => {
299 if !self.type_info[ty.index()]
301 .flags
302 .contains(super::TypeFlags::IO_SHAREABLE)
303 {
304 return Err(VaryingError::NotIOShareableType(ty));
305 }
306
307 if second_blend_source {
308 if !self
309 .capabilities
310 .contains(Capabilities::DUAL_SOURCE_BLENDING)
311 {
312 return Err(VaryingError::UnsupportedCapability(
313 Capabilities::DUAL_SOURCE_BLENDING,
314 ));
315 }
316 if self.stage != crate::ShaderStage::Fragment {
317 return Err(VaryingError::InvalidAttributeInStage(
318 "second_blend_source",
319 self.stage,
320 ));
321 }
322 if !self.output {
323 return Err(VaryingError::InvalidInputAttributeInStage(
324 "second_blend_source",
325 self.stage,
326 ));
327 }
328 if location != 0 {
329 return Err(VaryingError::InvalidLocationAttributeCombination {
330 location,
331 attribute: "second_blend_source",
332 });
333 }
334
335 self.second_blend_source = true;
336 } else if !self.location_mask.insert(location as usize) {
337 if self.flags.contains(super::ValidationFlags::BINDINGS) {
338 return Err(VaryingError::BindingCollision { location });
339 }
340 }
341
342 let needs_interpolation = match self.stage {
343 crate::ShaderStage::Vertex => self.output,
344 crate::ShaderStage::Fragment => !self.output,
345 crate::ShaderStage::Compute => false,
346 };
347
348 let _ = sampling;
352
353 let required = match sampling {
354 Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
355 _ => Capabilities::empty(),
356 };
357 if !self.capabilities.contains(required) {
358 return Err(VaryingError::UnsupportedCapability(required));
359 }
360
361 match ty_inner.scalar_kind() {
362 Some(crate::ScalarKind::Float) => {
363 if needs_interpolation && interpolation.is_none() {
364 return Err(VaryingError::MissingInterpolation);
365 }
366 }
367 Some(_) => {
368 if needs_interpolation && interpolation != Some(crate::Interpolation::Flat)
369 {
370 return Err(VaryingError::InvalidInterpolation);
371 }
372 }
373 None => return Err(VaryingError::InvalidType(ty)),
374 }
375 }
376 }
377
378 Ok(())
379 }
380
381 fn validate(
382 &mut self,
383 ep: &crate::EntryPoint,
384 ty: Handle<crate::Type>,
385 binding: Option<&crate::Binding>,
386 ) -> Result<(), WithSpan<VaryingError>> {
387 let span_context = self.types.get_span_context(ty);
388 match binding {
389 Some(binding) => self
390 .validate_impl(ep, ty, binding)
391 .map_err(|e| e.with_span_context(span_context)),
392 None => {
393 match self.types[ty].inner {
394 crate::TypeInner::Struct { ref members, .. } => {
395 for (index, member) in members.iter().enumerate() {
396 let span_context = self.types.get_span_context(ty);
397 match member.binding {
398 None => {
399 if self.flags.contains(super::ValidationFlags::BINDINGS) {
400 return Err(VaryingError::MemberMissingBinding(
401 index as u32,
402 )
403 .with_span_context(span_context));
404 }
405 }
406 Some(ref binding) => self
407 .validate_impl(ep, member.ty, binding)
408 .map_err(|e| e.with_span_context(span_context))?,
409 }
410 }
411 }
412 _ => {
413 if self.flags.contains(super::ValidationFlags::BINDINGS) {
414 return Err(VaryingError::MissingBinding.with_span());
415 }
416 }
417 }
418 Ok(())
419 }
420 }
421 }
422}
423
424impl super::Validator {
425 pub(super) fn validate_global_var(
426 &self,
427 var: &crate::GlobalVariable,
428 gctx: crate::proc::GlobalCtx,
429 mod_info: &ModuleInfo,
430 global_expr_kind: &crate::proc::ExpressionKindTracker,
431 ) -> Result<(), GlobalVariableError> {
432 use super::TypeFlags;
433
434 log::debug!("var {:?}", var);
435 let inner_ty = match gctx.types[var.ty].inner {
436 crate::TypeInner::BindingArray { base, .. } => match var.space {
440 crate::AddressSpace::Storage { .. }
441 | crate::AddressSpace::Uniform
442 | crate::AddressSpace::Handle => base,
443 _ => return Err(GlobalVariableError::InvalidUsage(var.space)),
444 },
445 _ => var.ty,
446 };
447 let type_info = &self.types[inner_ty.index()];
448
449 let (required_type_flags, is_resource) = match var.space {
450 crate::AddressSpace::Function => {
451 return Err(GlobalVariableError::InvalidUsage(var.space))
452 }
453 crate::AddressSpace::Storage { access } => {
454 if let Err((ty_handle, disalignment)) = type_info.storage_layout {
455 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
456 return Err(GlobalVariableError::Alignment(
457 var.space,
458 ty_handle,
459 disalignment,
460 ));
461 }
462 }
463 if access == crate::StorageAccess::STORE {
464 return Err(GlobalVariableError::StorageAddressSpaceWriteOnlyNotSupported);
465 }
466 (TypeFlags::DATA | TypeFlags::HOST_SHAREABLE, true)
467 }
468 crate::AddressSpace::Uniform => {
469 if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
470 if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) {
471 return Err(GlobalVariableError::Alignment(
472 var.space,
473 ty_handle,
474 disalignment,
475 ));
476 }
477 }
478 (
479 TypeFlags::DATA
480 | TypeFlags::COPY
481 | TypeFlags::SIZED
482 | TypeFlags::HOST_SHAREABLE,
483 true,
484 )
485 }
486 crate::AddressSpace::Handle => {
487 match gctx.types[inner_ty].inner {
488 crate::TypeInner::Image { class, .. } => match class {
489 crate::ImageClass::Storage {
490 format:
491 crate::StorageFormat::R16Unorm
492 | crate::StorageFormat::R16Snorm
493 | crate::StorageFormat::Rg16Unorm
494 | crate::StorageFormat::Rg16Snorm
495 | crate::StorageFormat::Rgba16Unorm
496 | crate::StorageFormat::Rgba16Snorm,
497 ..
498 } => {
499 if !self
500 .capabilities
501 .contains(Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS)
502 {
503 return Err(GlobalVariableError::UnsupportedCapability(
504 Capabilities::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
505 ));
506 }
507 }
508 _ => {}
509 },
510 crate::TypeInner::Sampler { .. }
511 | crate::TypeInner::AccelerationStructure
512 | crate::TypeInner::RayQuery => {}
513 _ => {
514 return Err(GlobalVariableError::InvalidType(var.space));
515 }
516 }
517
518 (TypeFlags::empty(), true)
519 }
520 crate::AddressSpace::Private => (TypeFlags::CONSTRUCTIBLE, false),
521 crate::AddressSpace::WorkGroup => (TypeFlags::DATA | TypeFlags::SIZED, false),
522 crate::AddressSpace::PushConstant => {
523 if !self.capabilities.contains(Capabilities::PUSH_CONSTANT) {
524 return Err(GlobalVariableError::UnsupportedCapability(
525 Capabilities::PUSH_CONSTANT,
526 ));
527 }
528 (
529 TypeFlags::DATA
530 | TypeFlags::COPY
531 | TypeFlags::HOST_SHAREABLE
532 | TypeFlags::SIZED,
533 false,
534 )
535 }
536 };
537
538 if !type_info.flags.contains(required_type_flags) {
539 return Err(GlobalVariableError::MissingTypeFlags {
540 seen: type_info.flags,
541 required: required_type_flags,
542 });
543 }
544
545 if is_resource != var.binding.is_some() {
546 if self.flags.contains(super::ValidationFlags::BINDINGS) {
547 return Err(GlobalVariableError::InvalidBinding);
548 }
549 }
550
551 if let Some(init) = var.init {
552 match var.space {
553 crate::AddressSpace::Private | crate::AddressSpace::Function => {}
554 _ => {
555 return Err(GlobalVariableError::InitializerNotAllowed(var.space));
556 }
557 }
558
559 if !global_expr_kind.is_const_or_override(init) {
560 return Err(GlobalVariableError::InitializerExprType);
561 }
562
563 let decl_ty = &gctx.types[var.ty].inner;
564 let init_ty = mod_info[init].inner_with(gctx.types);
565 if !decl_ty.equivalent(init_ty, gctx.types) {
566 return Err(GlobalVariableError::InitializerType);
567 }
568 }
569
570 Ok(())
571 }
572
573 pub(super) fn validate_entry_point(
574 &mut self,
575 ep: &crate::EntryPoint,
576 module: &crate::Module,
577 mod_info: &ModuleInfo,
578 global_expr_kind: &crate::proc::ExpressionKindTracker,
579 ) -> Result<FunctionInfo, WithSpan<EntryPointError>> {
580 if ep.early_depth_test.is_some() {
581 let required = Capabilities::EARLY_DEPTH_TEST;
582 if !self.capabilities.contains(required) {
583 return Err(
584 EntryPointError::Result(VaryingError::UnsupportedCapability(required))
585 .with_span(),
586 );
587 }
588
589 if ep.stage != crate::ShaderStage::Fragment {
590 return Err(EntryPointError::UnexpectedEarlyDepthTest.with_span());
591 }
592 }
593
594 if ep.stage == crate::ShaderStage::Compute {
595 if ep
596 .workgroup_size
597 .iter()
598 .any(|&s| s == 0 || s > MAX_WORKGROUP_SIZE)
599 {
600 return Err(EntryPointError::OutOfRangeWorkgroupSize.with_span());
601 }
602 } else if ep.workgroup_size != [0; 3] {
603 return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
604 }
605
606 let mut info = self
607 .validate_function(&ep.function, module, mod_info, true, global_expr_kind)
608 .map_err(WithSpan::into_other)?;
609
610 {
611 use super::ShaderStages;
612
613 let stage_bit = match ep.stage {
614 crate::ShaderStage::Vertex => ShaderStages::VERTEX,
615 crate::ShaderStage::Fragment => ShaderStages::FRAGMENT,
616 crate::ShaderStage::Compute => ShaderStages::COMPUTE,
617 };
618
619 if !info.available_stages.contains(stage_bit) {
620 return Err(EntryPointError::ForbiddenStageOperations.with_span());
621 }
622 }
623
624 self.location_mask.clear();
625 let mut argument_built_ins = crate::FastHashSet::default();
626 for (index, fa) in ep.function.arguments.iter().enumerate() {
628 let mut ctx = VaryingContext {
629 stage: ep.stage,
630 output: false,
631 second_blend_source: false,
632 types: &module.types,
633 type_info: &self.types,
634 location_mask: &mut self.location_mask,
635 built_ins: &mut argument_built_ins,
636 capabilities: self.capabilities,
637 flags: self.flags,
638 };
639 ctx.validate(ep, fa.ty, fa.binding.as_ref())
640 .map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
641 }
642
643 self.location_mask.clear();
644 if let Some(ref fr) = ep.function.result {
645 let mut result_built_ins = crate::FastHashSet::default();
646 let mut ctx = VaryingContext {
647 stage: ep.stage,
648 output: true,
649 second_blend_source: false,
650 types: &module.types,
651 type_info: &self.types,
652 location_mask: &mut self.location_mask,
653 built_ins: &mut result_built_ins,
654 capabilities: self.capabilities,
655 flags: self.flags,
656 };
657 ctx.validate(ep, fr.ty, fr.binding.as_ref())
658 .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
659 if ctx.second_blend_source {
660 if ctx.location_mask.len() == 1 && ctx.location_mask.contains(0) {
662 info.dual_source_blending = true;
663 } else {
664 return Err(EntryPointError::InvalidLocationsWhileDualSourceBlending {
665 location_mask: self.location_mask.clone(),
666 }
667 .with_span());
668 }
669 }
670
671 if ep.stage == crate::ShaderStage::Vertex
672 && !result_built_ins.contains(&crate::BuiltIn::Position { invariant: false })
673 {
674 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
675 }
676 } else if ep.stage == crate::ShaderStage::Vertex {
677 return Err(EntryPointError::MissingVertexOutputPosition.with_span());
678 }
679
680 {
681 let used_push_constants = module
682 .global_variables
683 .iter()
684 .filter(|&(_, var)| var.space == crate::AddressSpace::PushConstant)
685 .map(|(handle, _)| handle)
686 .filter(|&handle| !info[handle].is_empty());
687 #[allow(clippy::never_loop)]
690 for handle in used_push_constants.skip(1) {
691 return Err(EntryPointError::MoreThanOnePushConstantUsed
692 .with_span_handle(handle, &module.global_variables));
693 }
694 }
695
696 self.ep_resource_bindings.clear();
697 for (var_handle, var) in module.global_variables.iter() {
698 let usage = info[var_handle];
699 if usage.is_empty() {
700 continue;
701 }
702
703 let allowed_usage = match var.space {
704 crate::AddressSpace::Function => unreachable!(),
705 crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY,
706 crate::AddressSpace::Storage { access } => storage_usage(access),
707 crate::AddressSpace::Handle => match module.types[var.ty].inner {
708 crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner {
709 crate::TypeInner::Image {
710 class: crate::ImageClass::Storage { access, .. },
711 ..
712 } => storage_usage(access),
713 _ => GlobalUse::READ | GlobalUse::QUERY,
714 },
715 crate::TypeInner::Image {
716 class: crate::ImageClass::Storage { access, .. },
717 ..
718 } => storage_usage(access),
719 _ => GlobalUse::READ | GlobalUse::QUERY,
720 },
721 crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => GlobalUse::all(),
722 crate::AddressSpace::PushConstant => GlobalUse::READ,
723 };
724 if !allowed_usage.contains(usage) {
725 log::warn!("\tUsage error for: {:?}", var);
726 log::warn!(
727 "\tAllowed usage: {:?}, requested: {:?}",
728 allowed_usage,
729 usage
730 );
731 return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage)
732 .with_span_handle(var_handle, &module.global_variables));
733 }
734
735 if let Some(ref bind) = var.binding {
736 if !self.ep_resource_bindings.insert(bind.clone()) {
737 if self.flags.contains(super::ValidationFlags::BINDINGS) {
738 return Err(EntryPointError::BindingCollision(var_handle)
739 .with_span_handle(var_handle, &module.global_variables));
740 }
741 }
742 }
743 }
744
745 Ok(info)
746 }
747}