1use {
7 ash::vk::{self, Handle as _},
8 std::path::PathBuf,
9};
10
11pub const DLSS_RUNTIME_FILE: &str = "libnvidia-ngx-dlssd.so.310.4.0";
12pub const SDK_VERSION: &str = "310.4.0";
13
14#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
16#[repr(u32)]
17pub enum DlssRrColor {
18 #[default]
20 Hdr = 0,
21 Ldr = 1,
23}
24
25#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
28#[repr(C)]
29pub struct DlssRrConfig {
30 pub color: DlssRrColor,
31 pub motion_resolution: DlssRrMotionResolution,
32 pub depth: DlssRrDepth,
33}
34
35#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
36#[repr(u32)]
37pub enum DlssRrDepth {
38 #[default]
40 ReversedHardware = 0,
41 ForwardHardware = 1,
43 LinearViewSpace = 2,
45}
46
47#[derive(Clone, Copy, Debug, Default, PartialEq)]
48#[repr(C)]
49pub struct DlssRrFrameConstants {
50 pub camera_view_to_clip: [f32; 16],
51 pub clip_to_camera_view: [f32; 16],
52 pub clip_to_prev_clip: [f32; 16],
53 pub prev_clip_to_clip: [f32; 16],
54 pub world_to_camera_view: [f32; 16],
55 pub camera_view_to_world: [f32; 16],
56 pub jitter_offset: [f32; 2],
57 pub mvec_scale: [f32; 2],
63 pub camera_pos: [f32; 3],
64 pub camera_near: f32,
65 pub camera_up: [f32; 3],
66 pub camera_far: f32,
67 pub camera_right: [f32; 3],
68 pub camera_fov: f32,
69 pub camera_forward: [f32; 3],
70 pub camera_aspect_ratio: f32,
71 pub frame_index: u32,
72 pub reset: u32,
73}
74
75#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
77#[repr(u32)]
78pub enum DlssRrMotionResolution {
79 #[default]
80 Render = 0,
81 Output = 1,
83}
84
85#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
86#[repr(u32)]
87pub enum DlssRrQuality {
88 #[default]
89 Balanced = 0,
90 Quality = 1,
91 Dlaa = 2,
93}
94
95#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub struct DlssRrResources {
103 pub input_color: VulkanImage,
104 pub output_color: VulkanImage,
105 pub depth: VulkanImage,
106 pub motion: VulkanImage,
107 pub diffuse_albedo: VulkanImage,
108 pub specular_albedo: VulkanImage,
109 pub normal_roughness: VulkanImage,
110 pub reflection_guide: ReflectionGuide,
111}
112
113#[cfg(any(nvidia_dlss_native, test))]
114impl DlssRrResources {
115 fn native(&self, config: DlssRrConfig) -> anyhow::Result<NativeDlssRrResources> {
116 for (name, image) in [
117 ("input_color", self.input_color),
118 ("depth", self.depth),
119 ("motion", self.motion),
120 ("diffuse_albedo", self.diffuse_albedo),
121 ("specular_albedo", self.specular_albedo),
122 ("normal_roughness", self.normal_roughness),
123 ("reflection_guide", self.reflection_guide.image()),
124 ] {
125 anyhow::ensure!(
126 image.usage().contains(vk::ImageUsageFlags::SAMPLED),
127 "ngx {name} requires vk_image_usage_sampled_bit"
128 );
129
130 let extent =
131 if name == "motion" && config.motion_resolution == DlssRrMotionResolution::Output {
132 self.output_color.extent()
133 } else {
134 self.input_color.extent()
135 };
136
137 anyhow::ensure!(
138 image.extent() == extent && !extent.contains(&0),
139 "ngx {name} dimensions must match the configured resolution {extent:?}"
140 );
141 anyhow::ensure!(
142 image.layout() == vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
143 "ngx {name} must be in vk_image_layout_shader_read_only_optimal"
144 );
145 }
146
147 anyhow::ensure!(
148 self.output_color
149 .usage()
150 .contains(vk::ImageUsageFlags::STORAGE),
151 "ngx output_color requires vk_image_usage_storage_bit"
152 );
153 anyhow::ensure!(
154 !self.output_color.extent().contains(&0),
155 "zero output extent"
156 );
157
158 anyhow::ensure!(
159 self.output_color.layout() == vk::ImageLayout::GENERAL,
160 "ngx output_color must be in vk_image_layout_general"
161 );
162
163 let (reflection_guide, reflection_guide_kind) = match self.reflection_guide {
164 ReflectionGuide::SpecularMotion(image) => (image, 0),
165 ReflectionGuide::SpecularHitDistance(image) => {
166 anyhow::ensure!(
167 image.format() == vk::Format::R32_SFLOAT,
168 "specular hit distance must use vk_format_r32_sfloat"
169 );
170
171 (image, 1)
172 }
173 };
174
175 Ok(NativeDlssRrResources {
176 input_color: self.input_color,
177 output_color: self.output_color,
178 depth: self.depth,
179 motion: self.motion,
180 diffuse_albedo: self.diffuse_albedo,
181 specular_albedo: self.specular_albedo,
182 normal_roughness: self.normal_roughness,
183 reflection_guide,
184 reflection_guide_kind,
185 reserved: 0,
186 })
187 }
188}
189
190#[derive(Clone, Debug, Eq, PartialEq)]
191pub struct Identity {
192 kind: IdentityKind,
193}
194
195impl Identity {
196 pub fn application_id(application_id: u64) -> anyhow::Result<Self> {
199 anyhow::ensure!(
200 application_id != 0,
201 "nvidia application id must not be zero"
202 );
203
204 Ok(Self {
205 kind: IdentityKind::ApplicationId(application_id),
206 })
207 }
208
209 pub fn project(
212 project_id: impl Into<String>,
213 engine_version: impl Into<String>,
214 ) -> anyhow::Result<Self> {
215 let project_id = project_id.into();
216 let engine_version = engine_version.into();
217
218 anyhow::ensure!(
219 !project_id.is_empty(),
220 "nvidia project id must not be empty"
221 );
222 anyhow::ensure!(
223 !engine_version.is_empty(),
224 "nvidia engine version must not be empty"
225 );
226 anyhow::ensure!(
227 !project_id.as_bytes().contains(&0),
228 "nvidia project id contains a null byte"
229 );
230 anyhow::ensure!(
231 !engine_version.as_bytes().contains(&0),
232 "nvidia engine version contains a null byte"
233 );
234
235 Ok(Self {
236 kind: IdentityKind::Project {
237 project_id,
238 engine_version,
239 },
240 })
241 }
242
243 #[must_use]
244 pub fn application_id_value(&self) -> Option<u64> {
245 match self.kind {
246 IdentityKind::ApplicationId(value) => Some(value),
247 IdentityKind::Project { .. } => None,
248 }
249 }
250
251 #[must_use]
252 pub fn project_id(&self) -> Option<&str> {
253 match &self.kind {
254 IdentityKind::ApplicationId(_) => None,
255 IdentityKind::Project { project_id, .. } => Some(project_id),
256 }
257 }
258
259 #[must_use]
260 pub fn engine_version(&self) -> Option<&str> {
261 match &self.kind {
262 IdentityKind::ApplicationId(_) => None,
263 IdentityKind::Project { engine_version, .. } => Some(engine_version),
264 }
265 }
266}
267
268#[derive(Clone, Debug, Eq, PartialEq)]
269enum IdentityKind {
270 ApplicationId(u64),
271 Project {
272 project_id: String,
273 engine_version: String,
274 },
275}
276
277#[derive(Clone, Debug, Eq, PartialEq)]
278pub struct InitInfo {
279 pub identity: Identity,
280 pub application_data_path: PathBuf,
281 pub runtime_path: PathBuf,
282}
283
284impl InitInfo {
285 #[must_use]
286 pub fn new(
287 identity: Identity,
288 application_data_path: impl Into<PathBuf>,
289 runtime_path: impl Into<PathBuf>,
290 ) -> Self {
291 Self {
292 identity,
293 application_data_path: application_data_path.into(),
294 runtime_path: runtime_path.into(),
295 }
296 }
297}
298
299#[cfg(any(nvidia_dlss_native, test))]
300#[derive(Clone, Copy)]
301#[repr(C)]
302struct NativeDlssRrResources {
303 input_color: VulkanImage,
304 output_color: VulkanImage,
305 depth: VulkanImage,
306 motion: VulkanImage,
307 diffuse_albedo: VulkanImage,
308 specular_albedo: VulkanImage,
309 normal_roughness: VulkanImage,
310 reflection_guide: VulkanImage,
311 reflection_guide_kind: u32,
312 reserved: u32,
313}
314
315#[derive(Clone, Copy, Debug, Eq, PartialEq)]
316pub enum ReflectionGuide {
317 SpecularMotion(VulkanImage),
318 SpecularHitDistance(VulkanImage),
319}
320
321impl ReflectionGuide {
322 #[must_use]
323 pub fn image(self) -> VulkanImage {
324 match self {
325 Self::SpecularMotion(image) | Self::SpecularHitDistance(image) => image,
326 }
327 }
328}
329
330#[derive(Clone, Copy, Debug, Eq, PartialEq)]
331#[repr(C)]
332pub struct VulkanImage {
333 image: u64,
334 view: u64,
335 state: u32,
336 width: u32,
337 height: u32,
338 format: u32,
339 aspect_mask: u32,
340 base_mip_level: u32,
341 level_count: u32,
342 base_array_layer: u32,
343 layer_count: u32,
344 flags: u32,
345 usage: u32,
346}
347
348impl VulkanImage {
349 #[allow(clippy::too_many_arguments)]
353 #[must_use]
354 pub fn from_raw(
355 image: vk::Image,
356 view: vk::ImageView,
357 layout: vk::ImageLayout,
358 extent: [u32; 2],
359 format: vk::Format,
360 subresource_range: vk::ImageSubresourceRange,
361 flags: vk::ImageCreateFlags,
362 usage: vk::ImageUsageFlags,
363 ) -> Self {
364 Self {
365 image: image.as_raw(),
366 view: view.as_raw(),
367 state: layout.as_raw().cast_unsigned(),
368 width: extent[0],
369 height: extent[1],
370 format: format.as_raw().cast_unsigned(),
371 aspect_mask: subresource_range.aspect_mask.as_raw(),
372 base_mip_level: subresource_range.base_mip_level,
373 level_count: subresource_range.level_count,
374 base_array_layer: subresource_range.base_array_layer,
375 layer_count: subresource_range.layer_count,
376 flags: flags.as_raw(),
377 usage: usage.as_raw(),
378 }
379 }
380
381 #[must_use]
382 pub fn image(self) -> vk::Image {
383 vk::Image::from_raw(self.image)
384 }
385
386 #[must_use]
387 pub fn view(self) -> vk::ImageView {
388 vk::ImageView::from_raw(self.view)
389 }
390
391 #[must_use]
392 pub fn layout(self) -> vk::ImageLayout {
393 vk::ImageLayout::from_raw(self.state.cast_signed())
394 }
395
396 #[must_use]
397 pub fn extent(self) -> [u32; 2] {
398 [self.width, self.height]
399 }
400
401 #[must_use]
402 pub fn format(self) -> vk::Format {
403 vk::Format::from_raw(self.format.cast_signed())
404 }
405
406 #[must_use]
407 pub fn usage(self) -> vk::ImageUsageFlags {
408 vk::ImageUsageFlags::from_raw(self.usage)
409 }
410
411 pub fn subresource_range(self) -> vk::ImageSubresourceRange {
412 vk::ImageSubresourceRange {
413 aspect_mask: vk::ImageAspectFlags::from_raw(self.aspect_mask),
414 base_mip_level: self.base_mip_level,
415 level_count: self.level_count,
416 base_array_layer: self.base_array_layer,
417 layer_count: self.layer_count,
418 }
419 }
420
421 #[cfg(feature = "vk-graph")]
428 pub fn from_graph(
429 image: &vk_graph::driver::image::Image,
430 view: vk_graph::driver::image::ImageViewInfo,
431 layout: vk::ImageLayout,
432 ) -> anyhow::Result<Self> {
433 Ok(Self::from_raw(
434 image.handle,
435 image.view(view)?,
436 layout,
437 [
438 image
439 .info
440 .width
441 .checked_shr(view.base_mip_level)
442 .unwrap_or(0)
443 .max(1),
444 image
445 .info
446 .height
447 .checked_shr(view.base_mip_level)
448 .unwrap_or(0)
449 .max(1),
450 ],
451 view.format,
452 vk::ImageSubresourceRange {
453 aspect_mask: view.aspect_mask,
454 base_mip_level: view.base_mip_level,
455 level_count: view.mip_level_count,
456 base_array_layer: view.base_array_layer,
457 layer_count: view.array_layer_count,
458 },
459 image.info.flags,
460 image.info.usage,
461 ))
462 }
463}
464
465#[derive(Clone, Copy)]
467pub struct VulkanInitInfo {
468 pub instance: vk::Instance,
469 pub physical_device: vk::PhysicalDevice,
470 pub device: vk::Device,
471 pub get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
472 pub get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
473}
474
475#[cfg(nvidia_dlss_native)]
476mod native;
477#[cfg(not(nvidia_dlss_native))]
478mod stub;
479
480#[cfg(nvidia_dlss_native)]
481pub use native::{Evaluator, Ngx};
482#[cfg(not(nvidia_dlss_native))]
483pub use stub::{Evaluator, Ngx};
484
485#[cfg(test)]
486mod tests {
487 use {
488 super::*,
489 std::mem::{align_of, offset_of, size_of},
490 };
491
492 #[test]
493 fn backend_discovery() {
494 const AVAILABLE: bool = Ngx::native_backend_available();
495 let runtime_dir: Option<&'static std::path::Path> = Ngx::staged_runtime_dir();
496 assert_eq!(AVAILABLE, cfg!(nvidia_dlss_native));
497 assert_eq!(runtime_dir.is_some(), AVAILABLE);
498
499 #[cfg(nvidia_dlss_native)]
500 assert_eq!(
501 runtime_dir,
502 Some(std::path::Path::new(env!("NVIDIA_DLSS_RUNTIME_DIR")))
503 );
504 }
505
506 #[test]
507 fn vulkan_api_signatures() {
508 let _: unsafe fn(
509 &Ngx,
510 vk::Instance,
511 vk::PhysicalDevice,
512 ) -> anyhow::Result<Vec<std::ffi::CString>> = Ngx::device_extensions_raw;
513 let _: unsafe fn(&mut Ngx, VulkanInitInfo, DlssRrConfig) -> anyhow::Result<Evaluator> =
514 Ngx::initialize_raw;
515 let _: unsafe fn(
516 &Evaluator,
517 vk::CommandBuffer,
518 &DlssRrFrameConstants,
519 &DlssRrResources,
520 DlssRrQuality,
521 ) -> anyhow::Result<()> = Evaluator::evaluate;
522 let _: unsafe fn(&Evaluator) -> anyhow::Result<()> = Evaluator::shutdown;
523 let _: unsafe fn(&mut Ngx) -> anyhow::Result<()> = Ngx::shutdown;
524
525 #[cfg(feature = "vk-graph")]
526 {
527 use vk_graph::driver::{device::Device, instance::Instance};
528
529 let _: unsafe fn(
530 &Ngx,
531 &Instance,
532 vk::PhysicalDevice,
533 ) -> anyhow::Result<Vec<std::ffi::CString>> = Ngx::device_extensions;
534 let _: unsafe fn(&mut Ngx, &Device, DlssRrConfig) -> anyhow::Result<Evaluator> =
535 Ngx::initialize;
536 let _: fn(
537 &vk_graph::driver::image::Image,
538 vk_graph::driver::image::ImageViewInfo,
539 vk::ImageLayout,
540 ) -> anyhow::Result<VulkanImage> = VulkanImage::from_graph;
541 }
542 }
543
544 #[test]
545 fn native_ffi_layouts_are_stable() {
546 assert_eq!(size_of::<DlssRrConfig>(), 12);
547 assert_eq!(align_of::<DlssRrConfig>(), 4);
548 assert_eq!(offset_of!(DlssRrConfig, motion_resolution), 4);
549 assert_eq!(offset_of!(DlssRrConfig, depth), 8);
550 assert_eq!(DlssRrColor::Hdr as u32, 0);
551 assert_eq!(DlssRrColor::Ldr as u32, 1);
552 assert_eq!(DlssRrMotionResolution::Render as u32, 0);
553 assert_eq!(DlssRrMotionResolution::Output as u32, 1);
554 assert_eq!(DlssRrDepth::ReversedHardware as u32, 0);
555 assert_eq!(DlssRrDepth::ForwardHardware as u32, 1);
556 assert_eq!(DlssRrDepth::LinearViewSpace as u32, 2);
557 assert_eq!(
558 DlssRrConfig::default(),
559 DlssRrConfig {
560 color: DlssRrColor::Hdr,
561 motion_resolution: DlssRrMotionResolution::Render,
562 depth: DlssRrDepth::ReversedHardware,
563 }
564 );
565 assert_eq!(size_of::<DlssRrQuality>(), 4);
566 assert_eq!(DlssRrQuality::default(), DlssRrQuality::Balanced);
567 assert_eq!(DlssRrQuality::Balanced as u32, 0);
568 assert_eq!(DlssRrQuality::Quality as u32, 1);
569 assert_eq!(DlssRrQuality::Dlaa as u32, 2);
570
571 assert_eq!(size_of::<VulkanImage>(), 64);
572 assert_eq!(align_of::<VulkanImage>(), 8);
573 assert_eq!(offset_of!(VulkanImage, aspect_mask), 32);
574 assert_eq!(offset_of!(VulkanImage, base_mip_level), 36);
575 assert_eq!(offset_of!(VulkanImage, level_count), 40);
576 assert_eq!(offset_of!(VulkanImage, base_array_layer), 44);
577 assert_eq!(offset_of!(VulkanImage, layer_count), 48);
578 assert_eq!(offset_of!(VulkanImage, usage), 56);
579 assert_eq!(size_of::<NativeDlssRrResources>(), 520);
580 assert_eq!(
581 offset_of!(NativeDlssRrResources, reflection_guide_kind),
582 512
583 );
584 assert_eq!(size_of::<DlssRrFrameConstants>(), 472);
585 assert_eq!(offset_of!(DlssRrFrameConstants, mvec_scale), 392);
586 }
587
588 #[test]
589 fn raw_image_preserves_view_subresources() {
590 for aspect_mask in [
591 vk::ImageAspectFlags::COLOR,
592 vk::ImageAspectFlags::DEPTH,
593 vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
594 ] {
595 for (level_count, layer_count) in [
596 (2, 3),
597 (vk::REMAINING_MIP_LEVELS, vk::REMAINING_ARRAY_LAYERS),
598 ] {
599 let image = VulkanImage::from_raw(
600 vk::Image::from_raw(11),
601 vk::ImageView::from_raw(12),
602 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
603 [64, 32],
604 if aspect_mask == vk::ImageAspectFlags::COLOR {
605 vk::Format::R32_SFLOAT
606 } else {
607 vk::Format::D32_SFLOAT_S8_UINT
608 },
609 vk::ImageSubresourceRange {
610 aspect_mask,
611 base_mip_level: 4,
612 level_count,
613 base_array_layer: 5,
614 layer_count,
615 },
616 vk::ImageCreateFlags::empty(),
617 vk::ImageUsageFlags::SAMPLED,
618 );
619 let range = image.subresource_range();
620 assert_eq!(range.aspect_mask.as_raw(), aspect_mask.as_raw());
621 assert_eq!(range.base_mip_level, 4);
622 assert_eq!(range.level_count, level_count);
623 assert_eq!(range.base_array_layer, 5);
624 assert_eq!(range.layer_count, layer_count);
625 assert_eq!(image.image(), vk::Image::from_raw(11));
626 assert_eq!(image.view(), vk::ImageView::from_raw(12));
627 assert_eq!(image.extent(), [64, 32]);
628 assert_eq!(
629 image.format().as_raw(),
630 if aspect_mask == vk::ImageAspectFlags::COLOR {
631 vk::Format::R32_SFLOAT
632 } else {
633 vk::Format::D32_SFLOAT_S8_UINT
634 }
635 .as_raw()
636 );
637 }
638 }
639 }
640
641 #[test]
642 fn evaluation_requires_ngx_image_layouts() {
643 let input = VulkanImage::from_raw(
644 vk::Image::from_raw(1),
645 vk::ImageView::from_raw(2),
646 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
647 [64, 64],
648 vk::Format::R32_SFLOAT,
649 vk::ImageSubresourceRange::default()
650 .aspect_mask(vk::ImageAspectFlags::COLOR)
651 .level_count(1)
652 .layer_count(1),
653 vk::ImageCreateFlags::empty(),
654 vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE,
655 );
656 for reflection_guide in [
657 ReflectionGuide::SpecularMotion(input),
658 ReflectionGuide::SpecularHitDistance(input),
659 ] {
660 let resources = DlssRrResources {
661 input_color: input,
662 output_color: VulkanImage {
663 state: vk::ImageLayout::GENERAL.as_raw().cast_unsigned(),
664 ..input
665 },
666 depth: input,
667 motion: input,
668 diffuse_albedo: input,
669 specular_albedo: input,
670 normal_roughness: input,
671 reflection_guide,
672 };
673 assert!(resources.native(DlssRrConfig::default()).is_ok());
674 for index in 0..8 {
675 for layout in [
676 vk::ImageLayout::UNDEFINED,
677 vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
678 vk::ImageLayout::TRANSFER_DST_OPTIMAL,
679 vk::ImageLayout::GENERAL,
680 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
681 ] {
682 let mut changed = resources;
683 let guide = match &mut changed.reflection_guide {
684 ReflectionGuide::SpecularMotion(image)
685 | ReflectionGuide::SpecularHitDistance(image) => image,
686 };
687 let images = [
688 &mut changed.input_color,
689 &mut changed.output_color,
690 &mut changed.depth,
691 &mut changed.motion,
692 &mut changed.diffuse_albedo,
693 &mut changed.specular_albedo,
694 &mut changed.normal_roughness,
695 guide,
696 ];
697 images[index].state = layout.as_raw().cast_unsigned();
698 let required = if index == 1 {
699 vk::ImageLayout::GENERAL
700 } else {
701 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL
702 };
703 assert_eq!(
704 changed.native(DlssRrConfig::default()).is_ok(),
705 layout == required,
706 "image {index}"
707 );
708 }
709 }
710 }
711 }
712
713 #[test]
714 fn evaluation_requires_usage_and_configured_dimensions() {
715 let input = VulkanImage::from_raw(
716 vk::Image::from_raw(1),
717 vk::ImageView::from_raw(2),
718 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
719 [64, 32],
720 vk::Format::R32_SFLOAT,
721 vk::ImageSubresourceRange::default()
722 .aspect_mask(vk::ImageAspectFlags::COLOR)
723 .level_count(1)
724 .layer_count(1),
725 vk::ImageCreateFlags::empty(),
726 vk::ImageUsageFlags::SAMPLED,
727 );
728 let output = VulkanImage {
729 width: 128,
730 height: 64,
731 state: vk::ImageLayout::GENERAL.as_raw().cast_unsigned(),
732 usage: vk::ImageUsageFlags::STORAGE.as_raw(),
733 ..input
734 };
735 for motion_resolution in [
736 DlssRrMotionResolution::Render,
737 DlssRrMotionResolution::Output,
738 ] {
739 let config = DlssRrConfig {
740 motion_resolution,
741 ..DlssRrConfig::default()
742 };
743 for reflection_guide in [
744 ReflectionGuide::SpecularMotion(input),
745 ReflectionGuide::SpecularHitDistance(input),
746 ] {
747 let resources = DlssRrResources {
748 input_color: input,
749 output_color: output,
750 depth: input,
751 motion: if motion_resolution == DlssRrMotionResolution::Render {
752 input
753 } else {
754 VulkanImage {
755 width: 128,
756 height: 64,
757 ..input
758 }
759 },
760 diffuse_albedo: input,
761 specular_albedo: input,
762 normal_roughness: input,
763 reflection_guide,
764 };
765 assert!(resources.native(config).is_ok());
766 for index in 0..8 {
767 for invalid in 0..5 {
768 let mut changed = resources;
769 let guide = match &mut changed.reflection_guide {
770 ReflectionGuide::SpecularMotion(image)
771 | ReflectionGuide::SpecularHitDistance(image) => image,
772 };
773 let images = [
774 &mut changed.input_color,
775 &mut changed.output_color,
776 &mut changed.depth,
777 &mut changed.motion,
778 &mut changed.diffuse_albedo,
779 &mut changed.specular_albedo,
780 &mut changed.normal_roughness,
781 guide,
782 ];
783 let image = &mut *images[index];
784 match invalid {
785 0 => image.usage = 0,
786 1 => {
787 image.usage = if index == 1 {
788 vk::ImageUsageFlags::SAMPLED
789 } else {
790 vk::ImageUsageFlags::STORAGE
791 }
792 .as_raw();
793 }
794 2 => image.width = 0,
795 3 if index != 1 => image.width += 1,
796 4 if index != 1 => image.height += 1,
797 _ => continue,
798 }
799 assert!(
800 changed.native(config).is_err(),
801 "image {index}, invalid {invalid}"
802 );
803 }
804 }
805 }
806 }
807 }
808
809 #[test]
810 fn identity_requires_non_empty_values() {
811 assert!(Identity::application_id(0).is_err());
812 assert!(Identity::project("", "1.0").is_err());
813 assert!(Identity::project("project", "").is_err());
814 assert!(Identity::project("project", "1.0").is_ok());
815 }
816}