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[name.rfind("bind").expect("missing bind suffix")..]
1290 .parse()
1291 .unwrap_or_default();
1292 let rate = if name.contains("_ibind") {
1293 vk::VertexInputRate::INSTANCE
1294 } else {
1295 vk::VertexInputRate::VERTEX
1296 };
1297
1298 (binding, rate)
1299 })
1300 .unwrap_or_default();
1301 let (location, _) = location.into_inner();
1302 if let Some((input_rate, _)) = input_rates_strides.get(&binding) {
1303 assert_eq!(*input_rate, guessed_rate);
1304 }
1305
1306 let byte_stride = ty.nbyte().unwrap_or_default() as u32;
1307 let (input_rate, stride) = input_rates_strides.entry(binding).or_default();
1308 *input_rate = guessed_rate;
1309 *stride += byte_stride;
1310
1311 let format = match ty {
1314 Type::Scalar(ty) => scalar_format(ty),
1315 Type::Vector(ty) => vector_format(ty),
1316 _ => None,
1317 }
1318 .ok_or_else(|| {
1319 warn!("unsupported reflected vertex input type: {ty:?}");
1320
1321 DriverError::Unsupported
1322 })?;
1323
1324 vertex_attribute_descriptions.push(vk::VertexInputAttributeDescription {
1325 location,
1326 binding,
1327 format,
1328 offset: byte_stride,
1329 });
1330 }
1331
1332 vertex_attribute_descriptions.sort_unstable_by(|lhs, rhs| {
1333 let binding = lhs.binding.cmp(&rhs.binding);
1334 if binding.is_lt() {
1335 return binding;
1336 }
1337
1338 lhs.location.cmp(&rhs.location)
1339 });
1340
1341 let mut offset = 0;
1342 let mut offset_binding = 0;
1343
1344 for vertex_attribute_description in &mut vertex_attribute_descriptions {
1345 if vertex_attribute_description.binding != offset_binding {
1346 offset_binding = vertex_attribute_description.binding;
1347 offset = 0;
1348 }
1349
1350 let stride = vertex_attribute_description.offset;
1351 vertex_attribute_description.offset = offset;
1352 offset += stride;
1353
1354 debug!(
1355 "vertex attribute {}.{}: {:?} (offset={})",
1356 vertex_attribute_description.binding,
1357 vertex_attribute_description.location,
1358 vertex_attribute_description.format,
1359 vertex_attribute_description.offset,
1360 );
1361 }
1362
1363 let mut vertex_binding_descriptions = vec![];
1364 for (binding, (input_rate, stride)) in input_rates_strides.into_iter() {
1365 vertex_binding_descriptions.push(vk::VertexInputBindingDescription {
1366 binding,
1367 input_rate,
1368 stride,
1369 });
1370 }
1371
1372 Ok(VertexInputState {
1373 vertex_attribute_descriptions,
1374 vertex_binding_descriptions,
1375 })
1376 }
1377
1378 #[profiling::function]
1379 pub(super) fn vertex_input(&self) -> VertexInputState {
1380 self.try_vertex_input()
1381 .expect("unsupported reflected vertex input layout")
1382 }
1383}
1384
1385impl Debug for Shader {
1386 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1387 f.debug_struct(stringify!(Shader))
1389 .field("entry_name", &self.entry_name)
1390 .field("stage", &self.stage)
1391 .field("specialization", &self.specialization)
1392 .field("reflected_vars", &self.entry_point.vars.len())
1393 .finish_non_exhaustive()
1394 }
1395}
1396
1397impl TryFrom<ShaderBuilder> for Shader {
1398 type Error = DriverError;
1399
1400 fn try_from(shader: ShaderBuilder) -> Result<Self, Self::Error> {
1401 shader.try_build()
1402 }
1403}
1404
1405impl TryFrom<&[u8]> for Shader {
1406 type Error = DriverError;
1407
1408 fn try_from(spirv: &[u8]) -> Result<Self, Self::Error> {
1409 Shader::from_spirv(spirv).try_build()
1410 }
1411}
1412
1413impl TryFrom<&[u32]> for Shader {
1414 type Error = DriverError;
1415
1416 fn try_from(spirv: &[u32]) -> Result<Self, Self::Error> {
1417 Shader::from_spirv(spirv).try_build()
1418 }
1419}
1420
1421impl TryFrom<Vec<u8>> for Shader {
1422 type Error = DriverError;
1423
1424 fn try_from(spirv: Vec<u8>) -> Result<Self, Self::Error> {
1425 Shader::from_spirv(spirv).try_build()
1426 }
1427}
1428
1429impl TryFrom<Vec<u32>> for Shader {
1430 type Error = DriverError;
1431
1432 fn try_from(spirv: Vec<u32>) -> Result<Self, Self::Error> {
1433 Shader::from_spirv(spirv).try_build()
1434 }
1435}
1436
1437impl ShaderBuilder {
1439 pub fn new(stage: vk::ShaderStageFlags, spirv: Vec<u8>) -> Self {
1441 Self::default().stage(stage).spirv(spirv)
1442 }
1443
1444 pub fn build(self) -> Shader {
1450 self.try_build()
1451 .unwrap_or_else(|err| panic!("invalid or unsupported shader code: {err}"))
1452 }
1453
1454 #[profiling::function]
1474 pub fn image_sampler(
1475 mut self,
1476 descriptor: impl Into<Descriptor>,
1477 info: impl Into<SamplerInfo>,
1478 ) -> Self {
1479 let descriptor = descriptor.into();
1480 let info = info.into();
1481
1482 if self.image_samplers.is_none() {
1483 self.image_samplers = Some(Default::default());
1484 }
1485
1486 self.image_samplers
1487 .as_mut()
1488 .expect("missing image samplers")
1489 .insert(descriptor, info);
1490
1491 self
1492 }
1493
1494 pub fn try_build(mut self) -> Result<Shader, DriverError> {
1496 let entry_name = self.entry_name.as_deref().unwrap_or("main");
1497 let spirv = self
1498 .spirv
1499 .as_ref()
1500 .map(|spirv| spirv.words())
1501 .ok_or(DriverError::InvalidData)?;
1502 let specialization = self
1503 .specialization
1504 .as_ref()
1505 .map(|opt| opt.as_ref())
1506 .unwrap_or_default();
1507 let entry_point = Shader::reflect_entry_point(entry_name, spirv, specialization)?;
1508
1509 if self.stage.unwrap_or_default().is_empty() {
1510 self.stage = Some(match entry_point.exec_model {
1511 ExecutionModel::Vertex => vk::ShaderStageFlags::VERTEX,
1512 ExecutionModel::TessellationControl => vk::ShaderStageFlags::TESSELLATION_CONTROL,
1513 ExecutionModel::TessellationEvaluation => {
1514 vk::ShaderStageFlags::TESSELLATION_EVALUATION
1515 }
1516 ExecutionModel::Geometry => vk::ShaderStageFlags::GEOMETRY,
1517 ExecutionModel::Fragment => vk::ShaderStageFlags::FRAGMENT,
1518 ExecutionModel::GLCompute => vk::ShaderStageFlags::COMPUTE,
1519 ExecutionModel::Kernel => {
1520 warn!("unsupported shader execution model: kernel");
1521
1522 return Err(DriverError::Unsupported);
1523 }
1524 ExecutionModel::TaskNV => vk::ShaderStageFlags::TASK_EXT,
1525 ExecutionModel::MeshNV => vk::ShaderStageFlags::MESH_EXT,
1526 ExecutionModel::RayGenerationNV => vk::ShaderStageFlags::RAYGEN_KHR,
1527 ExecutionModel::IntersectionNV => vk::ShaderStageFlags::INTERSECTION_KHR,
1528 ExecutionModel::AnyHitNV => vk::ShaderStageFlags::ANY_HIT_KHR,
1529 ExecutionModel::ClosestHitNV => vk::ShaderStageFlags::CLOSEST_HIT_KHR,
1530 ExecutionModel::MissNV => vk::ShaderStageFlags::MISS_KHR,
1531 ExecutionModel::CallableNV => vk::ShaderStageFlags::CALLABLE_KHR,
1532 ExecutionModel::TaskEXT => vk::ShaderStageFlags::TASK_EXT,
1533 ExecutionModel::MeshEXT => vk::ShaderStageFlags::MESH_EXT,
1534 })
1535 }
1536
1537 self.entry_point = Some(entry_point);
1538
1539 self.fallible_build().map_err(|err| {
1540 warn!("invalid shader builder state: {err:?}");
1541
1542 DriverError::InvalidData
1543 })
1544 }
1545
1546 #[profiling::function]
1558 pub fn vertex_input(
1559 mut self,
1560 bindings: impl Into<Vec<vk::VertexInputBindingDescription>>,
1561 attributes: impl Into<Vec<vk::VertexInputAttributeDescription>>,
1562 ) -> Self {
1563 self.vertex_input_state = Some(Some(VertexInputState {
1564 vertex_binding_descriptions: bindings.into(),
1565 vertex_attribute_descriptions: attributes.into(),
1566 }));
1567 self
1568 }
1569}
1570
1571#[derive(Debug)]
1572struct ShaderBuilderError;
1573
1574impl From<UninitializedFieldError> for ShaderBuilderError {
1575 fn from(_: UninitializedFieldError) -> Self {
1576 Self
1577 }
1578}
1579
1580#[derive(Clone, Debug, Default)]
1582pub struct SpecializationMap {
1583 pub data: Vec<u8>,
1585
1586 pub entries: Vec<vk::SpecializationMapEntry>,
1589}
1590
1591impl SpecializationMap {
1592 pub fn new(data: impl Into<Vec<u8>>) -> Self {
1594 Self {
1595 data: data.into(),
1596 entries: Default::default(),
1597 }
1598 }
1599
1600 pub fn constant(mut self, constant_id: u32, offset: u32, size: usize) -> Self {
1602 self.set_constant(constant_id, offset, size);
1603 self
1604 }
1605
1606 pub fn set_constant(&mut self, constant_id: u32, offset: u32, size: usize) {
1608 self.entries.push(vk::SpecializationMapEntry {
1609 constant_id,
1610 offset,
1611 size,
1612 });
1613 }
1614}
1615
1616impl<'a> From<&'a SpecializationMap> for vk::SpecializationInfo<'a> {
1617 fn from(value: &'a SpecializationMap) -> Self {
1618 vk::SpecializationInfo::default()
1619 .map_entries(&value.entries)
1620 .data(&value.data)
1621 }
1622}
1623
1624#[cfg(test)]
1625mod test {
1626 use super::*;
1627
1628 type Info = SamplerInfo;
1629 type Builder = SamplerInfoBuilder;
1630
1631 #[test]
1632 pub fn sampler_info() {
1633 let info = Info::default();
1634 let builder = info.into_builder().build();
1635
1636 assert_eq!(info, builder);
1637 }
1638
1639 #[test]
1640 pub fn sampler_info_builder() {
1641 let info = Info::default();
1642 let builder = Builder::default().build();
1643
1644 assert_eq!(info, builder);
1645 }
1646
1647 #[test]
1648 pub fn invalid_spirv_try_into_driver_value() {
1649 assert!(Shader::try_from(vec![0u32]).is_err());
1650 }
1651
1652 #[test]
1653 pub fn merge_descriptor_bindings_rejects_mismatched_descriptors() {
1654 let mut lhs = DescriptorBindingMap::default();
1655 lhs.insert(
1656 Descriptor::from(0),
1657 (
1658 DescriptorInfo::UniformBuffer(1),
1659 vk::ShaderStageFlags::VERTEX,
1660 ),
1661 );
1662
1663 let mut rhs = DescriptorBindingMap::default();
1664 rhs.insert(
1665 Descriptor::from(0),
1666 (
1667 DescriptorInfo::StorageBuffer(1),
1668 vk::ShaderStageFlags::FRAGMENT,
1669 ),
1670 );
1671
1672 assert!(matches!(
1673 Shader::merge_descriptor_bindings([lhs, rhs]),
1674 Err(DriverError::InvalidData)
1675 ));
1676 }
1677}