1use {
97 super::{
98 DescriptorSetLayout, DriverError, VertexInputState,
99 descriptor_set_layout::{DescriptorSetLayoutBindingInfo, DescriptorSetLayoutInfo},
100 device::Device,
101 },
102 ash::vk,
103 derive_builder::{Builder, UninitializedFieldError},
104 log::{debug, error, trace, warn},
105 ordered_float::OrderedFloat,
106 spirq::{
107 ReflectConfig,
108 entry_point::EntryPoint,
109 parse::SpirvBinary,
110 spirv::ExecutionModel,
111 ty::{DescriptorType, ScalarType, Type, VectorType},
112 var::Variable,
113 },
114 std::{
115 collections::{BTreeMap, HashMap, HashSet},
116 fmt::{Debug, Formatter},
117 ops::Deref,
118 panic::{AssertUnwindSafe, catch_unwind},
119 thread::panicking,
120 },
121};
122
123pub(crate) type DescriptorBindingMap = HashMap<Descriptor, (DescriptorInfo, vk::ShaderStageFlags)>;
124
125#[profiling::function]
126fn guess_immutable_sampler(binding_name: &str) -> SamplerInfo {
127 const INVALID_ERR: &str = "Invalid sampler specification";
128
129 let (texel_filter, mipmap_mode, address_modes) = if binding_name.contains("_sampler_") {
130 let spec = &binding_name[binding_name.len() - 3..];
131 let texel_filter = match &spec[0..1] {
132 "n" => vk::Filter::NEAREST,
133 "l" => vk::Filter::LINEAR,
134 _ => panic!("{INVALID_ERR}: {}", &spec[0..1]),
135 };
136
137 let mipmap_mode = match &spec[1..2] {
138 "n" => vk::SamplerMipmapMode::NEAREST,
139 "l" => vk::SamplerMipmapMode::LINEAR,
140 _ => panic!("{INVALID_ERR}: {}", &spec[1..2]),
141 };
142
143 let address_modes = match &spec[2..3] {
144 "b" => vk::SamplerAddressMode::CLAMP_TO_BORDER,
145 "e" => vk::SamplerAddressMode::CLAMP_TO_EDGE,
146 "m" => vk::SamplerAddressMode::MIRRORED_REPEAT,
147 "r" => vk::SamplerAddressMode::REPEAT,
148 _ => panic!("{INVALID_ERR}: {}", &spec[2..3]),
149 };
150
151 (texel_filter, mipmap_mode, address_modes)
152 } else {
153 debug!("image binding {binding_name} using default sampler");
154
155 (
156 vk::Filter::LINEAR,
157 vk::SamplerMipmapMode::LINEAR,
158 vk::SamplerAddressMode::REPEAT,
159 )
160 };
161 let anisotropy_enable = texel_filter == vk::Filter::LINEAR;
162 let mut info = SamplerInfoBuilder::default()
163 .mag_filter(texel_filter)
164 .min_filter(texel_filter)
165 .mipmap_mode(mipmap_mode)
166 .address_mode_u(address_modes)
167 .address_mode_v(address_modes)
168 .address_mode_w(address_modes)
169 .max_lod(vk::LOD_CLAMP_NONE)
170 .anisotropy_enable(anisotropy_enable);
171
172 if anisotropy_enable {
173 info = info.max_anisotropy(16.0);
174 }
175
176 info.build()
177}
178
179#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
184pub struct Descriptor {
185 pub set: u32,
187
188 pub binding: u32,
190}
191
192impl From<u32> for Descriptor {
193 fn from(binding: u32) -> Self {
194 Self { set: 0, binding }
195 }
196}
197
198impl From<(u32, u32)> for Descriptor {
199 fn from((set, binding): (u32, u32)) -> Self {
200 Self { set, binding }
201 }
202}
203
204#[derive(Clone, Copy, Debug)]
205pub(crate) enum DescriptorInfo {
206 AccelerationStructure(u32),
207 CombinedImageSampler(u32, SamplerInfo, bool), InputAttachment(u32, u32), SampledImage(u32),
210 Sampler(u32, SamplerInfo, bool), StorageBuffer(u32),
212 StorageImage(u32),
213 StorageTexelBuffer(u32),
214 UniformBuffer(u32),
215 UniformTexelBuffer(u32),
216}
217
218impl DescriptorInfo {
219 pub fn binding_count(self) -> u32 {
220 match self {
221 Self::AccelerationStructure(binding_count) => binding_count,
222 Self::CombinedImageSampler(binding_count, ..) => binding_count,
223 Self::InputAttachment(binding_count, _) => binding_count,
224 Self::SampledImage(binding_count) => binding_count,
225 Self::Sampler(binding_count, ..) => binding_count,
226 Self::StorageBuffer(binding_count) => binding_count,
227 Self::StorageImage(binding_count) => binding_count,
228 Self::StorageTexelBuffer(binding_count) => binding_count,
229 Self::UniformBuffer(binding_count) => binding_count,
230 Self::UniformTexelBuffer(binding_count) => binding_count,
231 }
232 }
233
234 pub fn descriptor_type(self) -> vk::DescriptorType {
235 match self {
236 Self::AccelerationStructure(_) => vk::DescriptorType::ACCELERATION_STRUCTURE_KHR,
237 Self::CombinedImageSampler(..) => vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
238 Self::InputAttachment(..) => vk::DescriptorType::INPUT_ATTACHMENT,
239 Self::SampledImage(_) => vk::DescriptorType::SAMPLED_IMAGE,
240 Self::Sampler(..) => vk::DescriptorType::SAMPLER,
241 Self::StorageBuffer(_) => vk::DescriptorType::STORAGE_BUFFER,
242 Self::StorageImage(_) => vk::DescriptorType::STORAGE_IMAGE,
243 Self::StorageTexelBuffer(_) => vk::DescriptorType::STORAGE_TEXEL_BUFFER,
244 Self::UniformBuffer(_) => vk::DescriptorType::UNIFORM_BUFFER,
245 Self::UniformTexelBuffer(_) => vk::DescriptorType::UNIFORM_TEXEL_BUFFER,
246 }
247 }
248
249 fn sampler_info(self) -> Option<SamplerInfo> {
250 match self {
251 Self::CombinedImageSampler(_, sampler_info, _) | Self::Sampler(_, sampler_info, _) => {
252 Some(sampler_info)
253 }
254 _ => None,
255 }
256 }
257
258 pub fn set_binding_count(&mut self, binding_count: u32) {
259 *match self {
260 Self::AccelerationStructure(binding_count) => binding_count,
261 Self::CombinedImageSampler(binding_count, ..) => binding_count,
262 Self::InputAttachment(binding_count, _) => binding_count,
263 Self::SampledImage(binding_count) => binding_count,
264 Self::Sampler(binding_count, ..) => binding_count,
265 Self::StorageBuffer(binding_count) => binding_count,
266 Self::StorageImage(binding_count) => binding_count,
267 Self::StorageTexelBuffer(binding_count) => binding_count,
268 Self::UniformBuffer(binding_count) => binding_count,
269 Self::UniformTexelBuffer(binding_count) => binding_count,
270 } = binding_count;
271 }
272}
273
274#[derive(Debug)]
275pub(crate) struct PipelineDescriptorInfo {
276 pub layouts: BTreeMap<u32, DescriptorSetLayout>,
277 pub pool_sizes: HashMap<u32, HashMap<vk::DescriptorType, u32>>,
278}
279
280impl PipelineDescriptorInfo {
281 #[profiling::function]
282 pub fn create(
283 device: &Device,
284 descriptor_bindings: &DescriptorBindingMap,
285 bindless_descriptors: &HashSet<Descriptor>,
286 ) -> Result<Self, DriverError> {
287 let descriptor_set_count = descriptor_bindings
288 .keys()
289 .map(|descriptor| descriptor.set)
290 .max()
291 .map(|set| set + 1)
292 .unwrap_or_default();
293 let mut layouts = BTreeMap::new();
294 let mut pool_sizes = HashMap::new();
295
296 for descriptor_set_idx in 0..descriptor_set_count {
299 let mut binding_counts = HashMap::<vk::DescriptorType, u32>::new();
300 let mut bindings = vec![];
301
302 for (descriptor, (descriptor_info, stage_flags)) in descriptor_bindings
303 .iter()
304 .filter(|(descriptor, _)| descriptor.set == descriptor_set_idx)
305 {
306 let descriptor_ty = descriptor_info.descriptor_type();
307 *binding_counts.entry(descriptor_ty).or_default() +=
308 descriptor_info.binding_count();
309 bindings.push(DescriptorSetLayoutBindingInfo {
310 binding: descriptor.binding,
311 binding_flags: if bindless_descriptors.contains(descriptor)
312 && device
313 .physical
314 .features_v1_2
315 .descriptor_binding_partially_bound
316 {
317 vk::DescriptorBindingFlags::PARTIALLY_BOUND
318 } else {
319 vk::DescriptorBindingFlags::empty()
320 },
321 descriptor_count: descriptor_info.binding_count(),
322 descriptor_type: descriptor_ty,
323 immutable_sampler: descriptor_info.sampler_info(),
324 stage_flags: *stage_flags,
325 });
326 }
327
328 bindings.sort_unstable_by_key(|binding| binding.binding);
329
330 let pool_size = pool_sizes
331 .entry(descriptor_set_idx)
332 .or_insert_with(HashMap::new);
333
334 for (descriptor_ty, binding_count) in binding_counts.into_iter() {
335 *pool_size.entry(descriptor_ty).or_default() += binding_count;
336 }
337
338 layouts.insert(
339 descriptor_set_idx,
340 DescriptorSetLayout::get_or_create(
341 device,
342 DescriptorSetLayoutInfo {
343 bindings: bindings.into_boxed_slice(),
344 },
345 )?,
346 );
347 }
348
349 Ok(Self {
353 layouts,
354 pool_sizes,
355 })
356 }
357}
358
359pub(crate) struct Sampler {
360 device: Device,
361 sampler: vk::Sampler,
362}
363
364impl Sampler {
365 #[profiling::function]
366 pub fn create(device: &Device, info: impl Into<SamplerInfo>) -> Result<Self, DriverError> {
367 let device = device.clone();
368 let info = info.into();
369
370 let sampler = unsafe {
371 device
372 .create_sampler(
373 &vk::SamplerCreateInfo::default()
374 .flags(info.flags)
375 .mag_filter(info.mag_filter)
376 .min_filter(info.min_filter)
377 .mipmap_mode(info.mipmap_mode)
378 .address_mode_u(info.address_mode_u)
379 .address_mode_v(info.address_mode_v)
380 .address_mode_w(info.address_mode_w)
381 .mip_lod_bias(info.mip_lod_bias.0)
382 .anisotropy_enable(info.anisotropy_enable)
383 .max_anisotropy(info.max_anisotropy.0)
384 .compare_enable(info.compare_enable)
385 .compare_op(info.compare_op)
386 .min_lod(info.min_lod.0)
387 .max_lod(info.max_lod.0)
388 .border_color(info.border_color)
389 .unnormalized_coordinates(info.unnormalized_coordinates)
390 .push_next(
391 &mut vk::SamplerReductionModeCreateInfo::default()
392 .reduction_mode(info.reduction_mode),
393 ),
394 None,
395 )
396 .map_err(|err| match err {
397 vk::Result::ERROR_OUT_OF_HOST_MEMORY
398 | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
399 warn!("unable to create sampler: {err}");
400 DriverError::OutOfMemory
401 }
402 _ => {
403 warn!("unsupported sampler creation: {err}");
404 DriverError::Unsupported
405 }
406 })?
407 };
408
409 Ok(Self { device, sampler })
410 }
411}
412
413impl Debug for Sampler {
414 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
415 f.debug_struct(stringify!(Sampler))
416 .field("handle", &self.sampler)
417 .finish_non_exhaustive()
418 }
419}
420
421impl Deref for Sampler {
422 type Target = vk::Sampler;
423
424 fn deref(&self) -> &Self::Target {
425 &self.sampler
426 }
427}
428
429impl Drop for Sampler {
430 #[profiling::function]
431 fn drop(&mut self) {
432 if panicking() {
433 return;
434 }
435
436 unsafe {
437 self.device.destroy_sampler(self.sampler, None);
438 }
439 }
440}
441
442#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
446#[builder(
447 build_fn(private, name = "fallible_build", error = "SamplerInfoBuilderError"),
448 derive(Clone, Copy, Debug),
449 pattern = "owned"
450)]
451pub struct SamplerInfo {
452 #[builder(default)]
454 pub flags: vk::SamplerCreateFlags,
455
456 #[builder(default)]
460 pub mag_filter: vk::Filter,
461
462 #[builder(default)]
466 pub min_filter: vk::Filter,
467
468 #[builder(default)]
472 pub mipmap_mode: vk::SamplerMipmapMode,
473
474 #[builder(default)]
478 pub address_mode_u: vk::SamplerAddressMode,
479
480 #[builder(default)]
484 pub address_mode_v: vk::SamplerAddressMode,
485
486 #[builder(default)]
490 pub address_mode_w: vk::SamplerAddressMode,
491
492 #[builder(default, setter(into))]
495 pub mip_lod_bias: OrderedFloat<f32>,
496
497 #[builder(default)]
499 pub anisotropy_enable: bool,
500
501 #[builder(default, setter(into))]
505 pub max_anisotropy: OrderedFloat<f32>,
506
507 #[builder(default)]
509 pub compare_enable: bool,
510
511 #[builder(default)]
513 pub compare_op: vk::CompareOp,
514
515 #[builder(default, setter(into))]
517 pub min_lod: OrderedFloat<f32>,
518
519 #[builder(default, setter(into))]
523 pub max_lod: OrderedFloat<f32>,
524
525 #[builder(default)]
529 pub border_color: vk::BorderColor,
530
531 #[builder(default)]
541 pub unnormalized_coordinates: bool,
542
543 #[builder(default)]
552 pub reduction_mode: vk::SamplerReductionMode,
553}
554
555impl SamplerInfo {
556 pub const LINEAR: SamplerInfoBuilder = SamplerInfoBuilder {
558 flags: None,
559 mag_filter: Some(vk::Filter::LINEAR),
560 min_filter: Some(vk::Filter::LINEAR),
561 mipmap_mode: Some(vk::SamplerMipmapMode::LINEAR),
562 address_mode_u: None,
563 address_mode_v: None,
564 address_mode_w: None,
565 mip_lod_bias: None,
566 anisotropy_enable: None,
567 max_anisotropy: None,
568 compare_enable: None,
569 compare_op: None,
570 min_lod: None,
571 max_lod: None,
572 border_color: None,
573 unnormalized_coordinates: None,
574 reduction_mode: None,
575 };
576
577 pub const NEAREST: SamplerInfoBuilder = SamplerInfoBuilder {
580 flags: None,
581 mag_filter: Some(vk::Filter::NEAREST),
582 min_filter: Some(vk::Filter::NEAREST),
583 mipmap_mode: Some(vk::SamplerMipmapMode::NEAREST),
584 address_mode_u: None,
585 address_mode_v: None,
586 address_mode_w: None,
587 mip_lod_bias: None,
588 anisotropy_enable: None,
589 max_anisotropy: None,
590 compare_enable: None,
591 compare_op: None,
592 min_lod: None,
593 max_lod: None,
594 border_color: None,
595 unnormalized_coordinates: None,
596 reduction_mode: None,
597 };
598
599 pub fn builder() -> SamplerInfoBuilder {
601 Default::default()
602 }
603
604 pub fn into_builder(self) -> SamplerInfoBuilder {
606 SamplerInfoBuilder {
607 flags: Some(self.flags),
608 mag_filter: Some(self.mag_filter),
609 min_filter: Some(self.min_filter),
610 mipmap_mode: Some(self.mipmap_mode),
611 address_mode_u: Some(self.address_mode_u),
612 address_mode_v: Some(self.address_mode_v),
613 address_mode_w: Some(self.address_mode_w),
614 mip_lod_bias: Some(self.mip_lod_bias),
615 anisotropy_enable: Some(self.anisotropy_enable),
616 max_anisotropy: Some(self.max_anisotropy),
617 compare_enable: Some(self.compare_enable),
618 compare_op: Some(self.compare_op),
619 min_lod: Some(self.min_lod),
620 max_lod: Some(self.max_lod),
621 border_color: Some(self.border_color),
622 unnormalized_coordinates: Some(self.unnormalized_coordinates),
623 reduction_mode: Some(self.reduction_mode),
624 }
625 }
626}
627
628impl Default for SamplerInfo {
629 fn default() -> Self {
630 Self {
631 flags: vk::SamplerCreateFlags::empty(),
632 mag_filter: vk::Filter::NEAREST,
633 min_filter: vk::Filter::NEAREST,
634 mipmap_mode: vk::SamplerMipmapMode::NEAREST,
635 address_mode_u: vk::SamplerAddressMode::REPEAT,
636 address_mode_v: vk::SamplerAddressMode::REPEAT,
637 address_mode_w: vk::SamplerAddressMode::REPEAT,
638 mip_lod_bias: OrderedFloat(0.0),
639 anisotropy_enable: false,
640 max_anisotropy: OrderedFloat(0.0),
641 compare_enable: false,
642 compare_op: vk::CompareOp::NEVER,
643 min_lod: OrderedFloat(0.0),
644 max_lod: OrderedFloat(0.0),
645 border_color: vk::BorderColor::FLOAT_TRANSPARENT_BLACK,
646 unnormalized_coordinates: false,
647 reduction_mode: vk::SamplerReductionMode::WEIGHTED_AVERAGE,
648 }
649 }
650}
651
652impl SamplerInfoBuilder {
653 #[inline(always)]
655 pub fn build(self) -> SamplerInfo {
656 self.fallible_build().expect("invalid sampler info")
657 }
658}
659
660impl From<SamplerInfoBuilder> for SamplerInfo {
661 fn from(info: SamplerInfoBuilder) -> Self {
662 info.build()
663 }
664}
665
666#[derive(Debug)]
667struct SamplerInfoBuilderError;
668
669impl From<UninitializedFieldError> for SamplerInfoBuilderError {
670 fn from(_: UninitializedFieldError) -> Self {
671 Self
672 }
673}
674
675#[allow(missing_docs)]
680#[derive(Builder, Clone)]
681#[builder(
682 build_fn(private, name = "fallible_build", error = "ShaderBuilderError"),
683 derive(Clone, Debug),
684 pattern = "owned"
685)]
686pub struct Shader {
687 #[builder(default = "\"main\".to_owned()", setter(into))]
691 pub entry_name: String,
692
693 #[builder(default, setter(strip_option))]
736 pub specialization: Option<SpecializationMap>,
737
738 #[builder(setter(into))]
746 pub spirv: SpirvBinary,
747
748 pub stage: vk::ShaderStageFlags,
752
753 #[builder(private)]
754 entry_point: EntryPoint,
755
756 #[builder(default, private)]
757 image_samplers: HashMap<Descriptor, SamplerInfo>,
758
759 #[builder(default, private, setter(strip_option))]
760 vertex_input_state: Option<VertexInputState>,
761}
762
763macro_rules! shader_ctors {
764 ($(($name:ident, $flag:ident, $desc:literal),)*) => {
765 paste::paste! {
766 $(
767 #[doc = $desc]
768 pub fn [<new_ $name>](spirv: impl Into<SpirvBinary>) -> ShaderBuilder {
774 ShaderBuilder::default().spirv(spirv).stage(vk::ShaderStageFlags::$flag)
775 }
776
777 #[doc = "Creates a "]
778 #[doc = $desc]
779 #[doc = ", returning an error if the SPIR-V is invalid."]
780 pub fn [<try_new_ $name>](spirv: impl Into<SpirvBinary>) -> Result<Shader, DriverError> {
781 ShaderBuilder::default()
782 .spirv(spirv)
783 .stage(vk::ShaderStageFlags::$flag)
784 .try_build()
785 }
786 )*
787 }
788 }
789}
790
791impl Shader {
792 #[allow(clippy::new_ret_no_self)]
796 pub fn new(stage: vk::ShaderStageFlags, spirv: impl Into<SpirvBinary>) -> ShaderBuilder {
797 ShaderBuilder::default().spirv(spirv).stage(stage)
798 }
799
800 shader_ctors! {
801 (any_hit, ANY_HIT_KHR, "Creates a new ray tracing any-hit shader."),
802 (callable, CALLABLE_KHR, "Creates a new ray tracing callable shader."),
803 (closest_hit, CLOSEST_HIT_KHR, "Creates a new ray tracing closest-hit shader."),
804 (compute, COMPUTE, "Creates a new compute shader."),
805 (fragment, FRAGMENT, "Creates a new fragment shader."),
806 (geometry, GEOMETRY, "Creates a new geometry shader."),
807 (intersection, INTERSECTION_KHR, "Creates a new ray tracing intersection shader."),
808 (mesh, MESH_EXT, "Creates a new mesh shader."),
809 (miss, MISS_KHR, "Creates a new ray tracing miss shader."),
810 (ray_gen, RAYGEN_KHR, "Creates a new ray tracing ray-generation shader."),
811 (task, TASK_EXT, "Creates a new task shader."),
812 (tessellation_ctrl, TESSELLATION_CONTROL, "Creates a new tessellation control shader."),
813 (tessellation_eval, TESSELLATION_EVALUATION, "Creates a new tessellation evaluation shader."),
814 (vertex, VERTEX, "Creates a new vertex shader."),
815 }
816
817 #[profiling::function]
819 pub(super) fn attachments(
820 &self,
821 ) -> (
822 impl Iterator<Item = u32> + '_,
823 impl Iterator<Item = u32> + '_,
824 ) {
825 (
826 self.entry_point.vars.iter().filter_map(|var| match var {
827 Variable::Descriptor {
828 desc_ty: DescriptorType::InputAttachment(attachment),
829 ..
830 } => Some(*attachment),
831 _ => None,
832 }),
833 self.entry_point.vars.iter().filter_map(|var| match var {
834 Variable::Output { location, .. } => Some(location.loc()),
835 _ => None,
836 }),
837 )
838 }
839
840 pub fn builder() -> ShaderBuilder {
842 Default::default()
843 }
844
845 #[profiling::function]
846 pub(super) fn descriptor_bindings(&self) -> DescriptorBindingMap {
847 let mut res = DescriptorBindingMap::default();
848
849 for (name, descriptor, desc_ty, binding_count) in
850 self.entry_point.vars.iter().filter_map(|var| match var {
851 Variable::Descriptor {
852 name,
853 desc_bind,
854 desc_ty,
855 nbind,
856 ..
857 } => Some((
858 name,
859 Descriptor {
860 set: desc_bind.set(),
861 binding: desc_bind.bind(),
862 },
863 desc_ty,
864 *nbind,
865 )),
866 _ => None,
867 })
868 {
869 trace!(
870 "descriptor {}: {}.{} = {:?}[{}]",
871 name.as_deref().unwrap_or_default(),
872 descriptor.set,
873 descriptor.binding,
874 *desc_ty,
875 binding_count
876 );
877
878 let descriptor_info = match desc_ty {
879 DescriptorType::AccelStruct() => {
880 DescriptorInfo::AccelerationStructure(binding_count)
881 }
882 DescriptorType::CombinedImageSampler() => {
883 let (sampler_info, is_manually_defined) =
884 self.image_sampler(descriptor, name.as_deref().unwrap_or_default());
885
886 DescriptorInfo::CombinedImageSampler(
887 binding_count,
888 sampler_info,
889 is_manually_defined,
890 )
891 }
892 DescriptorType::InputAttachment(attachment) => {
893 DescriptorInfo::InputAttachment(binding_count, *attachment)
894 }
895 DescriptorType::SampledImage() => DescriptorInfo::SampledImage(binding_count),
896 DescriptorType::Sampler() => {
897 let (sampler_info, is_manually_defined) =
898 self.image_sampler(descriptor, name.as_deref().unwrap_or_default());
899
900 DescriptorInfo::Sampler(binding_count, sampler_info, is_manually_defined)
901 }
902 DescriptorType::StorageBuffer(_access_ty) => {
903 DescriptorInfo::StorageBuffer(binding_count)
904 }
905 DescriptorType::StorageImage(_access_ty) => {
906 DescriptorInfo::StorageImage(binding_count)
907 }
908 DescriptorType::StorageTexelBuffer(_access_ty) => {
909 DescriptorInfo::StorageTexelBuffer(binding_count)
910 }
911 DescriptorType::UniformBuffer() => DescriptorInfo::UniformBuffer(binding_count),
912 DescriptorType::UniformTexelBuffer() => {
913 DescriptorInfo::UniformTexelBuffer(binding_count)
914 }
915 };
916 res.insert(descriptor, (descriptor_info, self.stage));
917 }
918
919 res
920 }
921
922 pub fn from_spirv(spirv: impl Into<SpirvBinary>) -> ShaderBuilder {
926 ShaderBuilder::default().spirv(spirv)
927 }
928
929 fn image_sampler(&self, descriptor: Descriptor, name: &str) -> (SamplerInfo, bool) {
930 self.image_samplers
931 .get(&descriptor)
932 .copied()
933 .map(|sampler_info| (sampler_info, true))
934 .unwrap_or_else(|| (guess_immutable_sampler(name), false))
935 }
936
937 #[profiling::function]
938 pub(super) fn merge_descriptor_bindings(
939 descriptor_bindings: impl IntoIterator<Item = DescriptorBindingMap>,
940 ) -> Result<DescriptorBindingMap, DriverError> {
941 fn merge_info(lhs: &mut DescriptorInfo, rhs: DescriptorInfo) -> bool {
942 let (lhs_count, rhs_count) = match lhs {
943 DescriptorInfo::AccelerationStructure(lhs) => {
944 if let DescriptorInfo::AccelerationStructure(rhs) = rhs {
945 (lhs, rhs)
946 } else {
947 return false;
948 }
949 }
950 DescriptorInfo::CombinedImageSampler(lhs, lhs_sampler, lhs_is_manually_defined) => {
951 if let DescriptorInfo::CombinedImageSampler(
952 rhs,
953 rhs_sampler,
954 rhs_is_manually_defined,
955 ) = rhs
956 {
957 if *lhs_is_manually_defined && rhs_is_manually_defined {
959 return false;
960 } else if rhs_is_manually_defined {
961 *lhs_sampler = rhs_sampler;
962 }
963
964 (lhs, rhs)
965 } else {
966 return false;
967 }
968 }
969 DescriptorInfo::InputAttachment(lhs, lhs_idx) => {
970 if let DescriptorInfo::InputAttachment(rhs, rhs_idx) = rhs {
971 if *lhs_idx != rhs_idx {
972 return false;
973 }
974
975 (lhs, rhs)
976 } else {
977 return false;
978 }
979 }
980 DescriptorInfo::SampledImage(lhs) => {
981 if let DescriptorInfo::SampledImage(rhs) = rhs {
982 (lhs, rhs)
983 } else {
984 return false;
985 }
986 }
987 DescriptorInfo::Sampler(lhs, lhs_sampler, lhs_is_manually_defined) => {
988 if let DescriptorInfo::Sampler(rhs, rhs_sampler, rhs_is_manually_defined) = rhs
989 {
990 if *lhs_is_manually_defined && rhs_is_manually_defined {
992 return false;
993 } else if rhs_is_manually_defined {
994 *lhs_sampler = rhs_sampler;
995 }
996
997 (lhs, rhs)
998 } else {
999 return false;
1000 }
1001 }
1002 DescriptorInfo::StorageBuffer(lhs) => {
1003 if let DescriptorInfo::StorageBuffer(rhs) = rhs {
1004 (lhs, rhs)
1005 } else {
1006 return false;
1007 }
1008 }
1009 DescriptorInfo::StorageImage(lhs) => {
1010 if let DescriptorInfo::StorageImage(rhs) = rhs {
1011 (lhs, rhs)
1012 } else {
1013 return false;
1014 }
1015 }
1016 DescriptorInfo::StorageTexelBuffer(lhs) => {
1017 if let DescriptorInfo::StorageTexelBuffer(rhs) = rhs {
1018 (lhs, rhs)
1019 } else {
1020 return false;
1021 }
1022 }
1023 DescriptorInfo::UniformBuffer(lhs) => {
1024 if let DescriptorInfo::UniformBuffer(rhs) = rhs {
1025 (lhs, rhs)
1026 } else {
1027 return false;
1028 }
1029 }
1030 DescriptorInfo::UniformTexelBuffer(lhs) => {
1031 if let DescriptorInfo::UniformTexelBuffer(rhs) = rhs {
1032 (lhs, rhs)
1033 } else {
1034 return false;
1035 }
1036 }
1037 };
1038
1039 *lhs_count = rhs_count.max(*lhs_count);
1040
1041 true
1042 }
1043
1044 #[profiling::function]
1045 fn merge_pair(
1046 src: DescriptorBindingMap,
1047 dst: &mut DescriptorBindingMap,
1048 ) -> Result<(), DriverError> {
1049 for (descriptor_binding, (descriptor_info, descriptor_flags)) in src.into_iter() {
1050 if let Some((existing_info, existing_flags)) = dst.get_mut(&descriptor_binding) {
1051 if !merge_info(existing_info, descriptor_info) {
1052 warn!("inconsistent shader descriptors ({descriptor_binding:?})");
1053
1054 return Err(DriverError::InvalidData);
1055 }
1056
1057 *existing_flags |= descriptor_flags;
1058 } else {
1059 dst.insert(descriptor_binding, (descriptor_info, descriptor_flags));
1060 }
1061 }
1062
1063 Ok(())
1064 }
1065
1066 let mut descriptor_bindings = descriptor_bindings.into_iter();
1067 let mut res = descriptor_bindings.next().unwrap_or_default();
1068 for descriptor_binding in descriptor_bindings {
1069 merge_pair(descriptor_binding, &mut res)?;
1070 }
1071
1072 Ok(res)
1073 }
1074
1075 #[profiling::function]
1076 pub(super) fn push_constant_range(&self) -> Option<vk::PushConstantRange> {
1077 self.entry_point
1078 .vars
1079 .iter()
1080 .filter_map(|var| match var {
1081 Variable::PushConstant {
1082 ty: Type::Struct(ty),
1083 ..
1084 } => Some(ty.members.clone()),
1085 _ => None,
1086 })
1087 .flatten()
1088 .map(|push_const| {
1089 let offset = push_const.offset.unwrap_or_default();
1090 let size = push_const
1091 .ty
1092 .nbyte()
1093 .unwrap_or_default()
1094 .next_multiple_of(4);
1095 offset..offset + size
1096 })
1097 .reduce(|a, b| a.start.min(b.start)..a.end.max(b.end))
1098 .map(|push_const| vk::PushConstantRange {
1099 stage_flags: self.stage,
1100 size: (push_const.end - push_const.start) as _,
1101 offset: push_const.start as _,
1102 })
1103 }
1104
1105 #[profiling::function]
1106 fn reflect_entry_point(
1107 entry_name: &str,
1108 spirv: impl Into<SpirvBinary>,
1109 specialization: Option<&SpecializationMap>,
1110 ) -> Result<EntryPoint, DriverError> {
1111 catch_unwind(AssertUnwindSafe(|| {
1116 Self::reflect_entry_point_unchecked(entry_name, spirv, specialization)
1117 }))
1118 .map_err(|_| {
1119 warn!("invalid shader reflection entry point: panic");
1120
1121 DriverError::InvalidData
1122 })?
1123 .map_err(|err| {
1124 warn!("invalid shader reflection entry point: {err}");
1125
1126 DriverError::InvalidData
1127 })
1128 }
1129
1130 #[profiling::function]
1131 fn reflect_entry_point_unchecked(
1132 entry_name: &str,
1133 spirv: impl Into<SpirvBinary>,
1134 specialization: Option<&SpecializationMap>,
1135 ) -> Result<EntryPoint, DriverError> {
1136 let mut config = ReflectConfig::new();
1137 config.ref_all_rscs(true).spv(spirv);
1138
1139 if let Some(specialization) = specialization {
1140 for &vk::SpecializationMapEntry {
1141 constant_id,
1142 offset,
1143 size,
1144 } in &specialization.entries
1145 {
1146 config.specialize(
1147 constant_id,
1148 specialization.data[offset as usize..offset as usize + size].into(),
1149 );
1150 }
1151 }
1152
1153 let entry_points = config.reflect().map_err(|err| {
1154 error!("invalid SPIR-V reflection data: {err}");
1155
1156 DriverError::InvalidData
1157 })?;
1158 let entry_point = entry_points
1159 .into_iter()
1160 .find(|entry_point| entry_point.name == entry_name)
1161 .ok_or_else(|| {
1162 error!("invalid shader entry point: not found");
1163
1164 DriverError::InvalidData
1165 })?;
1166
1167 Ok(entry_point)
1168 }
1169
1170 #[profiling::function]
1171 pub(super) fn try_vertex_input(&self) -> Result<VertexInputState, DriverError> {
1172 if let Some(vertex_input) = &self.vertex_input_state {
1174 return Ok(vertex_input.clone());
1175 }
1176
1177 fn scalar_format(ty: &ScalarType) -> Option<vk::Format> {
1178 match *ty {
1179 ScalarType::Float { bits } => match bits {
1180 u8::BITS => Some(vk::Format::R8_SNORM),
1181 u16::BITS => Some(vk::Format::R16_SFLOAT),
1182 u32::BITS => Some(vk::Format::R32_SFLOAT),
1183 u64::BITS => Some(vk::Format::R64_SFLOAT),
1184 _ => None,
1185 },
1186 ScalarType::Integer {
1187 bits,
1188 is_signed: false,
1189 } => match bits {
1190 u8::BITS => Some(vk::Format::R8_UINT),
1191 u16::BITS => Some(vk::Format::R16_UINT),
1192 u32::BITS => Some(vk::Format::R32_UINT),
1193 u64::BITS => Some(vk::Format::R64_UINT),
1194 _ => None,
1195 },
1196 ScalarType::Integer {
1197 bits,
1198 is_signed: true,
1199 } => match bits {
1200 u8::BITS => Some(vk::Format::R8_SINT),
1201 u16::BITS => Some(vk::Format::R16_SINT),
1202 u32::BITS => Some(vk::Format::R32_SINT),
1203 u64::BITS => Some(vk::Format::R64_SINT),
1204 _ => None,
1205 },
1206 _ => None,
1207 }
1208 }
1209
1210 fn vector_format(ty: &VectorType) -> Option<vk::Format> {
1211 match *ty {
1212 VectorType {
1213 scalar_ty: ScalarType::Float { bits },
1214 nscalar,
1215 } => match (bits, nscalar) {
1216 (u8::BITS, 2) => Some(vk::Format::R8G8_SNORM),
1217 (u8::BITS, 3) => Some(vk::Format::R8G8B8_SNORM),
1218 (u8::BITS, 4) => Some(vk::Format::R8G8B8A8_SNORM),
1219 (u16::BITS, 2) => Some(vk::Format::R16G16_SFLOAT),
1220 (u16::BITS, 3) => Some(vk::Format::R16G16B16_SFLOAT),
1221 (u16::BITS, 4) => Some(vk::Format::R16G16B16A16_SFLOAT),
1222 (u32::BITS, 2) => Some(vk::Format::R32G32_SFLOAT),
1223 (u32::BITS, 3) => Some(vk::Format::R32G32B32_SFLOAT),
1224 (u32::BITS, 4) => Some(vk::Format::R32G32B32A32_SFLOAT),
1225 (u64::BITS, 2) => Some(vk::Format::R64G64_SFLOAT),
1226 (u64::BITS, 3) => Some(vk::Format::R64G64B64_SFLOAT),
1227 (u64::BITS, 4) => Some(vk::Format::R64G64B64A64_SFLOAT),
1228 _ => None,
1229 },
1230 VectorType {
1231 scalar_ty:
1232 ScalarType::Integer {
1233 bits,
1234 is_signed: false,
1235 },
1236 nscalar,
1237 } => match (bits, nscalar) {
1238 (u8::BITS, 2) => Some(vk::Format::R8G8_UINT),
1239 (u8::BITS, 3) => Some(vk::Format::R8G8B8_UINT),
1240 (u8::BITS, 4) => Some(vk::Format::R8G8B8A8_UINT),
1241 (u16::BITS, 2) => Some(vk::Format::R16G16_UINT),
1242 (u16::BITS, 3) => Some(vk::Format::R16G16B16_UINT),
1243 (u16::BITS, 4) => Some(vk::Format::R16G16B16A16_UINT),
1244 (u32::BITS, 2) => Some(vk::Format::R32G32_UINT),
1245 (u32::BITS, 3) => Some(vk::Format::R32G32B32_UINT),
1246 (u32::BITS, 4) => Some(vk::Format::R32G32B32A32_UINT),
1247 (u64::BITS, 2) => Some(vk::Format::R64G64_UINT),
1248 (u64::BITS, 3) => Some(vk::Format::R64G64B64_UINT),
1249 (u64::BITS, 4) => Some(vk::Format::R64G64B64A64_UINT),
1250 _ => None,
1251 },
1252 VectorType {
1253 scalar_ty:
1254 ScalarType::Integer {
1255 bits,
1256 is_signed: true,
1257 },
1258 nscalar,
1259 } => match (bits, nscalar) {
1260 (u8::BITS, 2) => Some(vk::Format::R8G8_SINT),
1261 (u8::BITS, 3) => Some(vk::Format::R8G8B8_SINT),
1262 (u8::BITS, 4) => Some(vk::Format::R8G8B8A8_SINT),
1263 (u16::BITS, 2) => Some(vk::Format::R16G16_SINT),
1264 (u16::BITS, 3) => Some(vk::Format::R16G16B16_SINT),
1265 (u16::BITS, 4) => Some(vk::Format::R16G16B16A16_SINT),
1266 (u32::BITS, 2) => Some(vk::Format::R32G32_SINT),
1267 (u32::BITS, 3) => Some(vk::Format::R32G32B32_SINT),
1268 (u32::BITS, 4) => Some(vk::Format::R32G32B32A32_SINT),
1269 (u64::BITS, 2) => Some(vk::Format::R64G64_SINT),
1270 (u64::BITS, 3) => Some(vk::Format::R64G64B64_SINT),
1271 (u64::BITS, 4) => Some(vk::Format::R64G64B64A64_SINT),
1272 _ => None,
1273 },
1274 _ => None,
1275 }
1276 }
1277
1278 let mut input_rates_strides = HashMap::new();
1279 let mut vertex_attribute_descriptions = vec![];
1280
1281 for (name, location, ty) in self.entry_point.vars.iter().filter_map(|var| match var {
1282 Variable::Input { name, location, ty } => Some((name, location, ty)),
1283 _ => None,
1284 }) {
1285 let (binding, guessed_rate) = name
1286 .as_ref()
1287 .filter(|name| name.contains("_ibind") || name.contains("_vbind"))
1288 .map(|name| {
1289 let binding = name
1290 [name.rfind("bind").expect("missing bind suffix") + "bind".len()..]
1291 .parse()
1292 .unwrap_or_default();
1293 let rate = if name.contains("_ibind") {
1294 vk::VertexInputRate::INSTANCE
1295 } else {
1296 vk::VertexInputRate::VERTEX
1297 };
1298
1299 (binding, rate)
1300 })
1301 .unwrap_or_default();
1302 let (location, _) = location.into_inner();
1303 if let Some((input_rate, _)) = input_rates_strides.get(&binding) {
1304 assert_eq!(*input_rate, guessed_rate);
1305 }
1306
1307 let byte_stride = ty.nbyte().unwrap_or_default() as u32;
1308 let (input_rate, stride) = input_rates_strides.entry(binding).or_default();
1309 *input_rate = guessed_rate;
1310 *stride += byte_stride;
1311
1312 let format = match ty {
1315 Type::Scalar(ty) => scalar_format(ty),
1316 Type::Vector(ty) => vector_format(ty),
1317 _ => None,
1318 }
1319 .ok_or_else(|| {
1320 warn!("unsupported reflected vertex input type: {ty:?}");
1321
1322 DriverError::Unsupported
1323 })?;
1324
1325 vertex_attribute_descriptions.push(vk::VertexInputAttributeDescription {
1326 location,
1327 binding,
1328 format,
1329 offset: byte_stride,
1330 });
1331 }
1332
1333 vertex_attribute_descriptions
1334 .sort_unstable_by_key(|attribute| (attribute.binding, attribute.location));
1335
1336 let mut offset = 0;
1337 let mut offset_binding = 0;
1338
1339 for vertex_attribute_description in &mut vertex_attribute_descriptions {
1340 if vertex_attribute_description.binding != offset_binding {
1341 offset_binding = vertex_attribute_description.binding;
1342 offset = 0;
1343 }
1344
1345 let stride = vertex_attribute_description.offset;
1346 vertex_attribute_description.offset = offset;
1347 offset += stride;
1348
1349 debug!(
1350 "vertex attribute {}.{}: {:?} (offset={})",
1351 vertex_attribute_description.binding,
1352 vertex_attribute_description.location,
1353 vertex_attribute_description.format,
1354 vertex_attribute_description.offset,
1355 );
1356 }
1357
1358 let mut vertex_binding_descriptions = vec![];
1359 for (binding, (input_rate, stride)) in input_rates_strides.into_iter() {
1360 vertex_binding_descriptions.push(vk::VertexInputBindingDescription {
1361 binding,
1362 input_rate,
1363 stride,
1364 });
1365 }
1366
1367 Ok(VertexInputState {
1368 vertex_attribute_descriptions,
1369 vertex_binding_descriptions,
1370 })
1371 }
1372
1373 #[profiling::function]
1374 pub(super) fn vertex_input(&self) -> VertexInputState {
1375 self.try_vertex_input()
1376 .expect("unsupported reflected vertex input layout")
1377 }
1378}
1379
1380impl Debug for Shader {
1381 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1382 f.debug_struct(stringify!(Shader))
1384 .field("entry_name", &self.entry_name)
1385 .field("stage", &self.stage)
1386 .field("specialization", &self.specialization)
1387 .field("reflected_vars", &self.entry_point.vars.len())
1388 .finish_non_exhaustive()
1389 }
1390}
1391
1392impl TryFrom<ShaderBuilder> for Shader {
1393 type Error = DriverError;
1394
1395 fn try_from(shader: ShaderBuilder) -> Result<Self, Self::Error> {
1396 shader.try_build()
1397 }
1398}
1399
1400impl TryFrom<&[u8]> for Shader {
1401 type Error = DriverError;
1402
1403 fn try_from(spirv: &[u8]) -> Result<Self, Self::Error> {
1404 Shader::from_spirv(spirv).try_build()
1405 }
1406}
1407
1408impl TryFrom<&[u32]> for Shader {
1409 type Error = DriverError;
1410
1411 fn try_from(spirv: &[u32]) -> Result<Self, Self::Error> {
1412 Shader::from_spirv(spirv).try_build()
1413 }
1414}
1415
1416impl TryFrom<Vec<u8>> for Shader {
1417 type Error = DriverError;
1418
1419 fn try_from(spirv: Vec<u8>) -> Result<Self, Self::Error> {
1420 Shader::from_spirv(spirv).try_build()
1421 }
1422}
1423
1424impl TryFrom<Vec<u32>> for Shader {
1425 type Error = DriverError;
1426
1427 fn try_from(spirv: Vec<u32>) -> Result<Self, Self::Error> {
1428 Shader::from_spirv(spirv).try_build()
1429 }
1430}
1431
1432impl ShaderBuilder {
1434 pub fn new(stage: vk::ShaderStageFlags, spirv: Vec<u8>) -> Self {
1436 Self::default().stage(stage).spirv(spirv)
1437 }
1438
1439 pub fn build(self) -> Shader {
1445 self.try_build()
1446 .unwrap_or_else(|err| panic!("invalid or unsupported shader code: {err}"))
1447 }
1448
1449 #[profiling::function]
1469 pub fn image_sampler(
1470 mut self,
1471 descriptor: impl Into<Descriptor>,
1472 info: impl Into<SamplerInfo>,
1473 ) -> Self {
1474 let descriptor = descriptor.into();
1475 let info = info.into();
1476
1477 if self.image_samplers.is_none() {
1478 self.image_samplers = Some(Default::default());
1479 }
1480
1481 self.image_samplers
1482 .as_mut()
1483 .expect("missing image samplers")
1484 .insert(descriptor, info);
1485
1486 self
1487 }
1488
1489 pub fn try_build(mut self) -> Result<Shader, DriverError> {
1491 let entry_name = self.entry_name.as_deref().unwrap_or("main");
1492 let spirv = self
1493 .spirv
1494 .as_ref()
1495 .map(|spirv| spirv.words())
1496 .ok_or(DriverError::InvalidData)?;
1497 let specialization = self
1498 .specialization
1499 .as_ref()
1500 .map(|opt| opt.as_ref())
1501 .unwrap_or_default();
1502 let entry_point = Shader::reflect_entry_point(entry_name, spirv, specialization)?;
1503
1504 if self.stage.unwrap_or_default().is_empty() {
1505 self.stage = Some(match entry_point.exec_model {
1506 ExecutionModel::Vertex => vk::ShaderStageFlags::VERTEX,
1507 ExecutionModel::TessellationControl => vk::ShaderStageFlags::TESSELLATION_CONTROL,
1508 ExecutionModel::TessellationEvaluation => {
1509 vk::ShaderStageFlags::TESSELLATION_EVALUATION
1510 }
1511 ExecutionModel::Geometry => vk::ShaderStageFlags::GEOMETRY,
1512 ExecutionModel::Fragment => vk::ShaderStageFlags::FRAGMENT,
1513 ExecutionModel::GLCompute => vk::ShaderStageFlags::COMPUTE,
1514 ExecutionModel::Kernel => {
1515 warn!("unsupported shader execution model: kernel");
1516
1517 return Err(DriverError::Unsupported);
1518 }
1519 ExecutionModel::TaskNV => vk::ShaderStageFlags::TASK_EXT,
1520 ExecutionModel::MeshNV => vk::ShaderStageFlags::MESH_EXT,
1521 ExecutionModel::RayGenerationNV => vk::ShaderStageFlags::RAYGEN_KHR,
1522 ExecutionModel::IntersectionNV => vk::ShaderStageFlags::INTERSECTION_KHR,
1523 ExecutionModel::AnyHitNV => vk::ShaderStageFlags::ANY_HIT_KHR,
1524 ExecutionModel::ClosestHitNV => vk::ShaderStageFlags::CLOSEST_HIT_KHR,
1525 ExecutionModel::MissNV => vk::ShaderStageFlags::MISS_KHR,
1526 ExecutionModel::CallableNV => vk::ShaderStageFlags::CALLABLE_KHR,
1527 ExecutionModel::TaskEXT => vk::ShaderStageFlags::TASK_EXT,
1528 ExecutionModel::MeshEXT => vk::ShaderStageFlags::MESH_EXT,
1529 })
1530 }
1531
1532 self.entry_point = Some(entry_point);
1533
1534 self.fallible_build().map_err(|err| {
1535 warn!("invalid shader builder state: {err:?}");
1536
1537 DriverError::InvalidData
1538 })
1539 }
1540
1541 #[profiling::function]
1553 pub fn vertex_input(
1554 mut self,
1555 bindings: impl Into<Vec<vk::VertexInputBindingDescription>>,
1556 attributes: impl Into<Vec<vk::VertexInputAttributeDescription>>,
1557 ) -> Self {
1558 self.vertex_input_state = Some(Some(VertexInputState {
1559 vertex_binding_descriptions: bindings.into(),
1560 vertex_attribute_descriptions: attributes.into(),
1561 }));
1562 self
1563 }
1564}
1565
1566#[derive(Debug)]
1567struct ShaderBuilderError;
1568
1569impl From<UninitializedFieldError> for ShaderBuilderError {
1570 fn from(_: UninitializedFieldError) -> Self {
1571 Self
1572 }
1573}
1574
1575#[derive(Clone, Debug, Default)]
1577pub struct SpecializationMap {
1578 pub data: Vec<u8>,
1580
1581 pub entries: Vec<vk::SpecializationMapEntry>,
1584}
1585
1586impl SpecializationMap {
1587 pub fn new(data: impl Into<Vec<u8>>) -> Self {
1589 Self {
1590 data: data.into(),
1591 entries: Default::default(),
1592 }
1593 }
1594
1595 pub fn constant(mut self, constant_id: u32, offset: u32, size: usize) -> Self {
1597 self.set_constant(constant_id, offset, size);
1598 self
1599 }
1600
1601 pub fn set_constant(&mut self, constant_id: u32, offset: u32, size: usize) {
1603 self.entries.push(vk::SpecializationMapEntry {
1604 constant_id,
1605 offset,
1606 size,
1607 });
1608 }
1609}
1610
1611impl<'a> From<&'a SpecializationMap> for vk::SpecializationInfo<'a> {
1612 fn from(value: &'a SpecializationMap) -> Self {
1613 vk::SpecializationInfo::default()
1614 .map_entries(&value.entries)
1615 .data(&value.data)
1616 }
1617}
1618
1619#[cfg(test)]
1620mod test {
1621 use super::*;
1622
1623 type Info = SamplerInfo;
1624 type Builder = SamplerInfoBuilder;
1625
1626 #[test]
1627 pub fn sampler_info() {
1628 let info = Info::default();
1629 let builder = info.into_builder().build();
1630
1631 assert_eq!(info, builder);
1632 }
1633
1634 #[test]
1635 pub fn sampler_info_builder() {
1636 let info = Info::default();
1637 let builder = Builder::default().build();
1638
1639 assert_eq!(info, builder);
1640 }
1641
1642 #[test]
1643 pub fn invalid_spirv_try_into_driver_value() {
1644 assert!(Shader::try_from(vec![0u32]).is_err());
1645 }
1646
1647 #[test]
1648 fn vertex_input_groups_bindings_before_assigning_offsets() {
1649 let shader = Shader {
1650 entry_name: "main".to_owned(),
1651 specialization: None,
1652 spirv: Vec::<u32>::new().into(),
1653 stage: vk::ShaderStageFlags::VERTEX,
1654 entry_point: EntryPoint {
1655 exec_model: ExecutionModel::Vertex,
1656 name: "main".to_owned(),
1657 vars: [("a_vbind0", 2), ("b_vbind1", 1), ("c_vbind0", 0)]
1659 .into_iter()
1660 .map(|(name, location)| Variable::Input {
1661 name: Some(name.to_owned()),
1662 location: spirq::var::InterfaceLocation::new(location, 0),
1663 ty: Type::Scalar(ScalarType::Float { bits: 32 }),
1664 })
1665 .collect(),
1666 exec_modes: vec![],
1667 },
1668 image_samplers: HashMap::new(),
1669 vertex_input_state: None,
1670 };
1671
1672 let input = shader.try_vertex_input().unwrap();
1673 let attributes: Vec<_> = input
1674 .vertex_attribute_descriptions
1675 .iter()
1676 .map(|attribute| (attribute.binding, attribute.location, attribute.offset))
1677 .collect();
1678
1679 assert_eq!(attributes, [(0, 0, 0), (0, 2, 4), (1, 1, 0)]);
1680 }
1681
1682 #[test]
1683 pub fn merge_descriptor_bindings_rejects_mismatched_descriptors() {
1684 let mut lhs = DescriptorBindingMap::default();
1685 lhs.insert(
1686 Descriptor::from(0),
1687 (
1688 DescriptorInfo::UniformBuffer(1),
1689 vk::ShaderStageFlags::VERTEX,
1690 ),
1691 );
1692
1693 let mut rhs = DescriptorBindingMap::default();
1694 rhs.insert(
1695 Descriptor::from(0),
1696 (
1697 DescriptorInfo::StorageBuffer(1),
1698 vk::ShaderStageFlags::FRAGMENT,
1699 ),
1700 );
1701
1702 assert!(matches!(
1703 Shader::merge_descriptor_bindings([lhs, rhs]),
1704 Err(DriverError::InvalidData)
1705 ));
1706 }
1707}