summary refs log tree commit diff
path: root/src/graphics/permanent.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/permanent.rs')
-rw-r--r--src/graphics/permanent.rs120
1 files changed, 97 insertions, 23 deletions
diff --git a/src/graphics/permanent.rs b/src/graphics/permanent.rs
index b39dbdd..6c4e02d 100644
--- a/src/graphics/permanent.rs
+++ b/src/graphics/permanent.rs
@@ -8,7 +8,7 @@ use vulkanalia::bytecode::Bytecode;
 use vulkanalia::loader::{ LibloadingLoader, LIBRARY };
 use vulkanalia::vk::{ self, HasBuilder,
                       ApplicationInfo, InstanceCreateInfo,
-                      DeviceV1_0, EntryV1_0, InstanceV1_0,
+                      DeviceV1_0, EntryV1_0, InstanceV1_0, InstanceV1_1,
                       ExtDebugUtilsExtensionInstanceCommands,
                       KhrSurfaceExtensionInstanceCommands };
 use winit::dpi::LogicalSize;
@@ -16,6 +16,7 @@ use winit::event_loop::ActiveEventLoop;
 use winit::window::{ Window, WindowAttributes };
 
 
+const VULKAN_MINIMUM_VERSION: Version = Version::new(1, 4, 0);
 const VULKAN_FIRST_PORTABILITY_VERSION: Version = Version::new(1, 3, 216);
 
 
@@ -135,7 +136,8 @@ impl Permanent {
         = init_vulkan_device(&instance, &surface,
                              enable_validation, enable_portability)?;
 
-    let descriptor_set_layout = init_descriptor_set_layout(&device)?;
+    let (primary_descriptor_set_layout, push_descriptor_set_layout)
+        = init_descriptor_set_layouts(&device)?;
 
     let (primary_command_pool, transient_command_pool)
             = init_command_pools(&device, &indices)?;
@@ -145,7 +147,8 @@ impl Permanent {
       graphics_queue, presentation_queue,
       primary_command_pool, transient_command_pool,
     }, ForReinit {
-      indices, sample_count, descriptor_set_layout,
+      indices, sample_count,
+      primary_descriptor_set_layout, push_descriptor_set_layout,
     }, enable_anisotropy, enable_swapchain))
   }
 
@@ -236,7 +239,8 @@ impl Permanent {
 pub struct ForReinit {
   pub indices: QueueFamilyIndices,
   pub sample_count: vk::SampleCountFlags,
-  pub descriptor_set_layout: vk::DescriptorSetLayout,
+  pub primary_descriptor_set_layout: vk::DescriptorSetLayout,
+  pub push_descriptor_set_layout: vk::DescriptorSetLayout,
 }
 
 
@@ -244,7 +248,13 @@ impl ForReinit {
   #[allow(unsafe_code)]
   pub fn destroy(self, device: &Device) -> () {
     unsafe {
-      device.destroy_descriptor_set_layout(self.descriptor_set_layout, None)
+      device.destroy_descriptor_set_layout(self.primary_descriptor_set_layout,
+                                           None)
+    };
+
+    unsafe {
+      device.destroy_descriptor_set_layout(self.push_descriptor_set_layout,
+                                           None)
     };
   }
 }
@@ -366,12 +376,28 @@ fn init_vulkan(window: &Window)
                this may mean other messages don't show up.");
   }
 
+  //   The api_version field here is the version of Vulkan we actually get.
+  // The official documentation is a bit misleading, since it says "maximum",
+  // but on close reading what it means is that we guarantee that we as the
+  // application will not require anything newer than that. So from our
+  // perspective it is the MINIMUM we will accept.
+  //
+  //   We define a constant for it to make it easy to find, since it's far
+  // more important operationally than any of the other versions declared
+  // here.
+  //
+  //   The validation layer enforces this API version on us, interposing
+  // itself when we attempt to load functions. Vulkanalia will panic!() on us
+  // when loading fails, with a message in the form "could not load
+  // vkWhateverWhatever", which can be quite confusing, so this comment text
+  // is here to make that easy to diagnose. If you got here for that reason,
+  // give thanks to the Waiting and its foresight.
   let application_info = ApplicationInfo::builder()
           .application_name(b"Surreality\0")
           .application_version(vk::make_version(1, 0, 0))
           .engine_name(b"Surreality\0")
           .engine_version(vk::make_version(1, 0, 0))
-          .api_version(vk::make_version(1, 0, 0));
+          .api_version(u32::from(VULKAN_MINIMUM_VERSION));
 
   //   Deceptively, this DOES get mutated later, but Vulkanalia doesn't see
   // it that way.
@@ -454,9 +480,24 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
   //   Old versions of Vulkan want layers to be enabled at the device
   // level as well. Newer ones will ignore this and just use the instance
   // layers.
-  let available_features = unsafe {
-    instance.get_physical_device_features(physical_device)
+  //
+  //   Dealing with this struct chain is kind of risky lifetime-wise, because
+  // Vulkanalia unsafely discards the reference for our "convenience". Alas.
+  let mut available_features_14
+          = vk::PhysicalDeviceVulkan14Features::default();
+  let mut available_features = vk::PhysicalDeviceFeatures2::builder()
+          .push_next(&mut available_features_14);
+  unsafe {
+    instance.get_physical_device_features2(physical_device,
+                                           &mut available_features)
   };
+
+  //   Another struct chain with a risky lifetime. Vulkanalia's mutation
+  // features return a copy, so we have to avoid mutating features_14 once we
+  // add it to the chain. Fortunately we only need it for one thing right now,
+  // so we do that here at the top and treat it as non-mutable.
+  let mut features_14 = vk::PhysicalDeviceVulkan14Features::builder()
+          .push_descriptor(true);
   let mut features = vk::PhysicalDeviceFeatures::builder();
   let mut extensions = Vec::new();
   let mut layers = Vec::new();
@@ -506,7 +547,7 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
     EnableSwapchain(false)
   };
 
-  let enable_anisotropy = if available_features.sampler_anisotropy
+  let enable_anisotropy = if available_features.features.sampler_anisotropy
                              == vk::TRUE
   {
     features = features.sampler_anisotropy(true);
@@ -535,11 +576,14 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
                     .queue_priorities(&[1.0]));
   }
 
+  let mut features2 = vk::PhysicalDeviceFeatures2::builder()
+          .features(features);
   let device_info = vk::DeviceCreateInfo::builder()
           .queue_create_infos(&queues)
           .enabled_layer_names(&layers)
           .enabled_extension_names(&extensions)
-          .enabled_features(&features);
+          .push_next(&mut features2)
+          .push_next(&mut features_14);
 
   let device = unsafe {
     instance.create_device(physical_device, &device_info, None)
@@ -564,8 +608,8 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
 
 
 #[allow(unsafe_code)]
-fn init_descriptor_set_layout(device: &Device)
-    -> Result<vk::DescriptorSetLayout>
+fn init_descriptor_set_layouts(device: &Device)
+    -> Result<(vk::DescriptorSetLayout, vk::DescriptorSetLayout)>
 {
   let uniform_block_binding = vk::DescriptorSetLayoutBinding::builder()
           .binding(0)
@@ -579,21 +623,32 @@ fn init_descriptor_set_layout(device: &Device)
           .descriptor_count(1)
           .stage_flags(vk::ShaderStageFlags::FRAGMENT);
 
+  let primary_bindings = [uniform_block_binding, sampler_binding];
+  let primary_descriptor_set_layout_info
+          = vk::DescriptorSetLayoutCreateInfo::builder()
+                .bindings(&primary_bindings);
+  let primary_descriptor_set_layout = unsafe {
+    device.create_descriptor_set_layout(&primary_descriptor_set_layout_info,
+                                        None)
+  }?;
+
   let texture_binding = vk::DescriptorSetLayoutBinding::builder()
-          .binding(2)
+          .binding(0)
           .descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
           .descriptor_count(1)
           .stage_flags(vk::ShaderStageFlags::FRAGMENT);
 
-  let bindings = [uniform_block_binding, sampler_binding, texture_binding];
-  let descriptor_set_layout_info
+  let push_bindings = [texture_binding];
+  let push_descriptor_set_layout_info
           = vk::DescriptorSetLayoutCreateInfo::builder()
-                .bindings(&bindings);
-  let descriptor_set_layout = unsafe {
-    device.create_descriptor_set_layout(&descriptor_set_layout_info, None)
+                .bindings(&push_bindings)
+                .flags(vk::DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR);
+  let push_descriptor_set_layout = unsafe {
+    device.create_descriptor_set_layout(&push_descriptor_set_layout_info,
+                                        None)
   }?;
 
-  Ok(descriptor_set_layout)
+  Ok((primary_descriptor_set_layout, push_descriptor_set_layout))
 }
 
 
@@ -770,10 +825,15 @@ fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
     // selected.
   }
 
-  let features = unsafe {
-    instance.get_physical_device_features(*physical_device)
+  let mut features_14
+          = vk::PhysicalDeviceVulkan14Features::default();
+  let mut features = vk::PhysicalDeviceFeatures2::builder()
+          .push_next(&mut features_14);
+  unsafe {
+    instance.get_physical_device_features2(*physical_device, &mut features)
   };
-  if features.sampler_anisotropy == vk::TRUE {
+
+  if features.features.sampler_anisotropy == vk::TRUE {
     score += 1;
   }
 
@@ -786,7 +846,17 @@ fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
   score += shift;
   let sample_count = vk::SampleCountFlags::from_bits(1 << shift).unwrap();
 
-  Ok(Acceptable::Accepted((score, indices, sample_count)))
+  //   We prefer to report the most important reason, rather than
+  // less-fundamental ones. To make that easy, we try to keep this logic
+  // together at the end. Note that, before we get here, we may have already
+  // rejected the device thanks to find_device_queue_family_indices(), called
+  // above, but that's okay because all those reasons are in fact more
+  // important than the ones tested here.
+  if features_14.push_descriptor != vk::TRUE {
+    Ok(Acceptable::Rejected("Doesn't support push descriptors.".to_string()))
+  } else {
+    Ok(Acceptable::Accepted((score, indices, sample_count)))
+  }
 }
 
 
@@ -819,6 +889,9 @@ fn find_device_queue_family_indices(instance: &Instance,
     }
   }
 
+  //   We prefer to report the most important reason, rather than
+  // less-fundamental ones. To make that easy, we keep all this logic together
+  // at the end.
   if let Some(graphics) = graphics {
     if let Some(presentation) = presentation {
       Ok(Acceptable::Accepted(QueueFamilyIndices {
@@ -885,3 +958,4 @@ extern "system" fn debug_messager_callback(
   // test so anything it does is fine with us.
   vk::FALSE
 }
+