vidya/crates/renderer/src/lib.rs
2024-12-15 01:59:19 +01:00

2325 lines
86 KiB
Rust

#![feature(c_str_module, closure_lifetime_binder, let_chains)]
#![allow(unused)]
use std::{
collections::{BTreeMap, BTreeSet, HashMap},
ffi::{CStr, CString},
fmt::Debug,
marker::PhantomData,
sync::{Arc, Mutex},
};
use ash::{khr, vk, Entry};
use dyn_clone::DynClone;
use tinyvec::{array_vec, ArrayVec};
use winit::raw_window_handle::DisplayHandle;
mod commands;
mod images;
mod sync;
type VkAllocator = Arc<vk_mem::Allocator>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
LoadingError(#[from] ash::LoadingError),
#[error(transparent)]
Result(#[from] ash::vk::Result),
#[error(transparent)]
CStrError(#[from] core::ffi::c_str::FromBytesUntilNulError),
#[error(transparent)]
NulError(#[from] std::ffi::NulError),
#[error("No Physical Device found.")]
NoPhysicalDevice,
}
type Result<T> = core::result::Result<T, Error>;
struct VkNameList<'a> {
names: Vec<*const i8>,
_pd: PhantomData<&'a ()>,
}
impl<'a> VkNameList<'a> {
fn from_strs(strs: &[&'a CStr]) -> Self {
let names = strs.iter().map(|str| str.as_ptr()).collect::<Vec<_>>();
Self {
names,
_pd: PhantomData,
}
}
}
#[derive(Debug, Clone)]
struct DeviceExtension<'a> {
name: &'a core::ffi::CStr,
version: u32,
}
fn make_extention_properties(
name: &CStr,
version: u32,
) -> vk::ExtensionProperties {
vk::ExtensionProperties::default()
.spec_version(version)
.extension_name(name)
.unwrap()
}
/// returns true if lhs and rhs have the same name and lhs spec_version is less
/// than or equal to rhs spec_version
fn compatible_extension_properties(
lhs: &vk::ExtensionProperties,
rhs: &vk::ExtensionProperties,
) -> bool {
let Some(lhs_name) = lhs.extension_name_as_c_str().ok() else {
return false;
};
let Some(rhs_name) = rhs.extension_name_as_c_str().ok() else {
return false;
};
if lhs_name == rhs_name {
lhs.spec_version <= rhs.spec_version
} else {
false
}
}
#[derive(Clone, Debug)]
struct Queue(Arc<Mutex<vk::Queue>>);
impl Queue {
fn new(device: &ash::Device, family: u32, index: u32) -> Self {
Self(Arc::new(Mutex::new(unsafe {
device.get_device_queue(family, index)
})))
}
pub fn with_locked<T, F: FnOnce(vk::Queue) -> T>(&self, map: F) -> T {
let lock = self.0.lock().expect("mutex lock poison");
map(*lock)
}
pub fn lock(&self) -> std::sync::MutexGuard<'_, vk::Queue> {
self.0.lock().unwrap()
}
}
trait ExtendsDeviceFeatures2Debug:
vk::ExtendsPhysicalDeviceFeatures2 + Debug
{
}
trait ExtendsDeviceProperties2Debug:
vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone
{
}
impl<T: vk::ExtendsPhysicalDeviceFeatures2 + Debug> ExtendsDeviceFeatures2Debug
for T
{
}
impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone>
ExtendsDeviceProperties2Debug for T
{
}
#[derive(Default, Debug)]
struct PhysicalDeviceFeatures {
version: u32,
physical_features_10: vk::PhysicalDeviceFeatures,
physical_features_11: Option<vk::PhysicalDeviceVulkan11Features<'static>>,
physical_features_12: Option<vk::PhysicalDeviceVulkan12Features<'static>>,
physical_features_13: Option<vk::PhysicalDeviceVulkan13Features<'static>>,
extra_features: Vec<Box<dyn ExtendsDeviceFeatures2Debug>>,
device_extensions: Vec<vk::ExtensionProperties>,
}
impl PhysicalDeviceFeatures {
fn version(self, version: u32) -> Self {
Self { version, ..self }
}
fn all_default() -> Self {
Self::default()
.features11(Default::default())
.features12(Default::default())
.features13(Default::default())
}
fn query(
instance: &ash::Instance,
pdev: vk::PhysicalDevice,
) -> Result<Self> {
let mut this = Self::all_default();
let mut features2 = this.features2();
let features = unsafe {
instance.get_physical_device_features2(pdev, &mut features2);
// allocate and query again
features2.features
};
this = this.features10(features);
let extensions =
unsafe { instance.enumerate_device_extension_properties(pdev)? };
this = this.device_extensions(extensions);
Ok(this)
}
fn features10(
self,
physical_features_10: vk::PhysicalDeviceFeatures,
) -> Self {
Self {
physical_features_10,
..self
}
}
fn features11(
self,
physical_features_11: vk::PhysicalDeviceVulkan11Features<'static>,
) -> Self {
Self {
physical_features_11: Some(physical_features_11),
..self
}
}
fn features12(
self,
physical_features_12: vk::PhysicalDeviceVulkan12Features<'static>,
) -> Self {
Self {
physical_features_12: Some(physical_features_12),
..self
}
}
fn features13(
self,
physical_features_13: vk::PhysicalDeviceVulkan13Features<'static>,
) -> Self {
Self {
physical_features_13: Some(physical_features_13),
..self
}
}
fn device_extensions(
self,
device_extensions: Vec<vk::ExtensionProperties>,
) -> Self {
Self {
device_extensions,
..self
}
}
fn with_extension2(mut self, ext: vk::ExtensionProperties) -> Self {
self.device_extensions.push(ext);
self
}
fn with_extensions2<I: IntoIterator<Item = vk::ExtensionProperties>>(
mut self,
exts: I,
) -> Self {
self.device_extensions.extend(exts);
self
}
fn with_extension<F>(
mut self,
ext: vk::ExtensionProperties,
features: F,
) -> Self
where
F: ExtendsDeviceFeatures2Debug + 'static,
{
self.extra_features.push(Box::new(features));
self.device_extensions.push(ext);
self
}
fn features2(&mut self) -> vk::PhysicalDeviceFeatures2<'_> {
let mut features2 = vk::PhysicalDeviceFeatures2::default()
.features(self.physical_features_10);
if let Some(ref mut features11) = self.physical_features_11 {
features2 = features2.push_next(features11);
}
if let Some(ref mut features12) = self.physical_features_12 {
features2 = features2.push_next(features12);
}
if let Some(ref mut features13) = self.physical_features_13 {
features2 = features2.push_next(features13);
}
for features in self.extra_features.iter_mut() {
features2 = features2.push_next(Box::as_mut(features));
}
features2
}
fn compatible_with(&self, device: &Self) -> bool {
let sort_exts = |a: &vk::ExtensionProperties,
b: &vk::ExtensionProperties| {
(a.extension_name_as_c_str().unwrap(), a.spec_version)
.cmp(&(b.extension_name_as_c_str().unwrap(), b.spec_version))
};
let mut device_extensions = device.device_extensions.clone();
device_extensions.sort_by(sort_exts);
let unsupported_extensions = self
.device_extensions
.iter()
.filter(|ext| {
!device_extensions
.binary_search_by(|t| sort_exts(t, ext))
.is_ok()
})
.cloned()
.collect::<Vec<_>>();
let supports_extensions = unsupported_extensions.is_empty();
supports_extensions
&& utils::eq_device_features10(
&utils::bitand_device_features10(
&self.physical_features_10,
&device.physical_features_10,
),
&self.physical_features_10,
)
&& self
.physical_features_11
.zip(device.physical_features_11)
.map(|(a, b)| {
utils::eq_device_features11(
&utils::bitand_device_features11(&a, &b),
&a,
)
})
.unwrap_or(true)
&& self
.physical_features_12
.zip(device.physical_features_12)
.map(|(a, b)| {
utils::eq_device_features12(
&utils::bitand_device_features12(&a, &b),
&a,
)
})
.unwrap_or(true)
&& self
.physical_features_13
.zip(device.physical_features_13)
.map(|(a, b)| {
utils::eq_device_features13(
&utils::bitand_device_features13(&a, &b),
&a,
)
})
.unwrap_or(true)
}
}
#[derive(Debug, Default)]
struct PhysicalDeviceProperties {
base: vk::PhysicalDeviceProperties,
vk11: vk::PhysicalDeviceVulkan11Properties<'static>,
vk12: vk::PhysicalDeviceVulkan12Properties<'static>,
vk13: vk::PhysicalDeviceVulkan13Properties<'static>,
extra_properties: Vec<Box<dyn ExtendsDeviceProperties2Debug>>,
}
impl PhysicalDeviceProperties {
fn query(&mut self, instance: &ash::Instance, pdev: vk::PhysicalDevice) {
let mut props2 = self.properties2();
unsafe {
instance.get_physical_device_properties2(pdev, &mut props2);
self.base = props2.properties;
}
}
fn extra_properties(
mut self,
extra_properties: Vec<Box<dyn ExtendsDeviceProperties2Debug>>,
) -> Self {
self.extra_properties = extra_properties;
self
}
fn with_properties<F>(mut self, properties: F) -> Self
where
F: ExtendsDeviceProperties2Debug + 'static,
{
self.extra_properties.push(Box::new(properties));
self
}
fn properties2(&mut self) -> vk::PhysicalDeviceProperties2<'_> {
let mut props2 = vk::PhysicalDeviceProperties2::default()
.properties(self.base)
.push_next(&mut self.vk11)
.push_next(&mut self.vk12)
.push_next(&mut self.vk13);
for props in &mut self.extra_properties {
props2 = props2.push_next(Box::as_mut(props));
}
props2
}
}
#[derive(Debug)]
struct PhysicalDevice {
pdev: vk::PhysicalDevice,
queue_families: DeviceQueueFamilies,
properties: PhysicalDeviceProperties,
}
impl PhysicalDevice {
fn swapchain_family_indices(&self) -> ArrayVec<[u32; 2]> {
let mut indices = array_vec!([u32; 2] => self.queue_families.graphics);
if let Some(present) = self.queue_families.present
&& present != self.queue_families.graphics
{
indices.push(present);
}
indices
}
}
struct Instance {
entry: Entry,
instance: ash::Instance,
debug_utils: ash::ext::debug_utils::Instance,
debug_utils_messenger: vk::DebugUtilsMessengerEXT,
surface: ash::khr::surface::Instance,
}
impl Drop for Instance {
fn drop(&mut self) {
unsafe {
self.debug_utils.destroy_debug_utils_messenger(
self.debug_utils_messenger,
None,
);
}
}
}
impl AsRef<ash::Instance> for Instance {
fn as_ref(&self) -> &ash::Instance {
&self.instance
}
}
impl AsRef<ash::khr::surface::Instance> for Instance {
fn as_ref(&self) -> &ash::khr::surface::Instance {
&self.surface
}
}
#[derive(Debug, Default)]
struct DeviceQueueFamilies {
graphics: u32,
present: Option<u32>,
async_compute: Option<u32>,
transfer: Option<u32>,
}
struct Device {
physical: PhysicalDevice,
device: ash::Device,
swapchain: khr::swapchain::Device,
main_queue: Queue,
compute_queue: Queue,
transfer_queue: Queue,
present_queue: Queue,
}
#[derive(Clone)]
struct Device2(Arc<Device>);
impl Device2 {
fn dev(&self) -> &ash::Device {
&self.0.device
}
fn swapchain(&self) -> &khr::swapchain::Device {
&self.0.swapchain
}
fn queue_families(&self) -> &DeviceQueueFamilies {
&self.0.physical.queue_families
}
fn phy(&self) -> &vk::PhysicalDevice {
&self.0.physical.pdev
}
fn graphics_queue(&self) -> &Queue {
&self.0.main_queue
}
fn present_queue(&self) -> &Queue {
&self.0.present_queue
}
}
impl AsRef<khr::swapchain::Device> for Device2 {
fn as_ref(&self) -> &khr::swapchain::Device {
&self.0.swapchain
}
}
impl AsRef<ash::Device> for Device2 {
fn as_ref(&self) -> &ash::Device {
&self.0.device
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
_ = self.device.device_wait_idle();
self.device.destroy_device(None);
}
}
}
struct DeviceAndQueues {
device: Arc<Device>,
main_queue: Queue,
compute_queue: Queue,
transfer_queue: Queue,
present_queue: Queue,
}
impl AsRef<ash::Device> for DeviceAndQueues {
fn as_ref(&self) -> &ash::Device {
&self.device.device
}
}
impl AsRef<ash::khr::swapchain::Device> for DeviceAndQueues {
fn as_ref(&self) -> &ash::khr::swapchain::Device {
&self.device.swapchain
}
}
struct Swapchain {
instance: Arc<Instance>,
device: Arc<Device>,
// has a strong ref to the surface because the surface may not outlive the swapchain
surface: Arc<Surface>,
swapchain: vk::SwapchainKHR,
present_mode: vk::PresentModeKHR,
color_space: vk::ColorSpaceKHR,
format: vk::Format,
images: Vec<vk::Image>,
image_views: Vec<vk::ImageView>,
extent: vk::Extent2D,
}
impl Drop for Swapchain {
fn drop(&mut self) {
unsafe {
for view in &self.image_views {
self.device.device.destroy_image_view(*view, None);
}
self.device
.swapchain
.destroy_swapchain(self.swapchain, None);
}
}
}
fn current_extent_or_clamped(
caps: &vk::SurfaceCapabilitiesKHR,
fallback: vk::Extent2D,
) -> vk::Extent2D {
if caps.current_extent.width == u32::MAX {
vk::Extent2D {
width: fallback.width.clamp(
caps.min_image_extent.width,
caps.max_image_extent.width,
),
height: fallback.height.clamp(
caps.min_image_extent.height,
caps.max_image_extent.height,
),
}
} else {
caps.current_extent
}
}
impl Swapchain {
fn get_swapchain_params_from_surface(
instance: &Arc<Instance>,
surface: vk::SurfaceKHR,
pdev: vk::PhysicalDevice,
requested_extent: vk::Extent2D,
) -> Result<(
vk::PresentModeKHR,
vk::Format,
vk::ColorSpaceKHR,
u32,
vk::Extent2D,
)> {
let caps = unsafe {
instance
.surface
.get_physical_device_surface_capabilities(pdev, surface)?
};
let formats = unsafe {
instance
.surface
.get_physical_device_surface_formats(pdev, surface)?
};
let present_modes = unsafe {
instance
.surface
.get_physical_device_surface_present_modes(pdev, surface)?
};
let present_mode = present_modes
.iter()
.find(|&mode| mode == &vk::PresentModeKHR::MAILBOX)
.cloned()
.unwrap_or(vk::PresentModeKHR::FIFO);
let format = formats
.iter()
.max_by_key(|&&format| {
let is_rgba_unorm = format.format == vk::Format::R8G8B8A8_UNORM
|| format.format == vk::Format::B8G8R8A8_UNORM;
let is_srgb =
format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR;
is_rgba_unorm as u8 * 10 + is_srgb as u8
})
.or(formats.first())
.cloned()
.expect("no surface format available!");
let image_count = 3u32.clamp(
caps.min_image_count,
if caps.max_image_count == 0 {
u32::MAX
} else {
caps.max_image_count
},
);
let extent = current_extent_or_clamped(&caps, requested_extent);
Ok((
present_mode,
format.format,
format.color_space,
image_count,
extent,
))
}
fn create_new(
instance: Arc<Instance>,
device: Arc<Device>,
surface: Arc<Surface>,
pdev: vk::PhysicalDevice,
extent: vk::Extent2D,
) -> Result<Self> {
let (present_mode, format, color_space, image_count, extent) =
Self::get_swapchain_params_from_surface(
&instance,
surface.surface,
pdev,
extent,
)?;
let (swapchain, images) = Self::create_vkswapchainkhr(
&device,
surface.surface,
&device.physical.swapchain_family_indices(),
extent,
None,
present_mode,
format,
color_space,
image_count,
)?;
let image_views = images
.iter()
.map(|&image| {
let info = vk::ImageViewCreateInfo::default()
.image(image)
.format(format)
.components(vk::ComponentMapping::default())
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(1),
);
unsafe { device.device.create_image_view(&info, None) }
})
.collect::<core::result::Result<Vec<vk::ImageView>, _>>()?;
Ok(Self {
instance,
device,
surface,
swapchain,
present_mode,
color_space,
format,
images,
image_views,
extent,
})
}
fn recreate(&self, extent: vk::Extent2D) -> Result<Self> {
let (present_mode, format, color_space, image_count, extent) =
Self::get_swapchain_params_from_surface(
&self.instance,
self.surface.surface,
self.device.physical.pdev,
extent,
)?;
let (swapchain, images) = Self::create_vkswapchainkhr(
&self.device,
self.surface.surface,
&self.device.physical.swapchain_family_indices(),
extent,
Some(self.swapchain),
present_mode,
format,
color_space,
image_count,
)?;
let image_views = images
.iter()
.map(|&image| {
let info = vk::ImageViewCreateInfo::default()
.image(image)
.format(format)
.components(vk::ComponentMapping::default())
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(1),
);
unsafe { self.device.device.create_image_view(&info, None) }
})
.collect::<core::result::Result<Vec<vk::ImageView>, _>>()?;
Ok(Self {
instance: self.instance.clone(),
device: self.device.clone(),
swapchain,
surface: self.surface.clone(),
present_mode,
color_space,
format,
images,
image_views,
extent,
})
}
fn create_vkswapchainkhr(
device: &Arc<Device>,
surface: vk::SurfaceKHR,
queue_families: &[u32],
image_extent: vk::Extent2D,
old_swapchain: Option<vk::SwapchainKHR>,
present_mode: vk::PresentModeKHR,
image_format: vk::Format,
image_color_space: vk::ColorSpaceKHR,
image_count: u32,
) -> Result<(vk::SwapchainKHR, Vec<vk::Image>)> {
let create_info = vk::SwapchainCreateInfoKHR::default()
.surface(surface)
.present_mode(present_mode)
.image_color_space(image_color_space)
.image_format(image_format)
.min_image_count(image_count)
.image_usage(vk::ImageUsageFlags::TRANSFER_DST)
.image_array_layers(1)
.image_extent(image_extent)
.image_sharing_mode(if queue_families.len() <= 1 {
vk::SharingMode::EXCLUSIVE
} else {
vk::SharingMode::CONCURRENT
})
.queue_family_indices(queue_families)
.pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY)
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
.old_swapchain(old_swapchain.unwrap_or(vk::SwapchainKHR::null()))
.clipped(true);
let (swapchain, images) = unsafe {
let swapchain =
device.swapchain.create_swapchain(&create_info, None)?;
let images = device.swapchain.get_swapchain_images(swapchain)?;
(swapchain, images)
};
Ok((swapchain, images))
}
}
struct Surface {
instance: Arc<Instance>,
surface: vk::SurfaceKHR,
}
impl Surface {
fn create(
instance: Arc<Instance>,
display_handle: winit::raw_window_handle::RawDisplayHandle,
window_handle: winit::raw_window_handle::RawWindowHandle,
) -> Result<Self> {
let surface = unsafe {
ash_window::create_surface(
&instance.entry,
&instance.instance,
display_handle,
window_handle,
None,
)?
};
Ok(Self { instance, surface })
}
}
impl Drop for Surface {
fn drop(&mut self) {
unsafe {
self.instance.surface.destroy_surface(self.surface, None);
}
}
}
pub struct Vulkan {
instance: Arc<Instance>,
device: Arc<Device>,
alloc: VkAllocator,
}
impl Vulkan {
const VALIDATION_LAYER_NAME: &'static core::ffi::CStr =
c"VK_LAYER_KHRONOS_validation";
#[allow(unused)]
const RENDERDOC_LAYER_NAME: &'static core::ffi::CStr =
c"VK_LAYER_RENDERDOC_Capture";
#[allow(unused)]
const NSIGHT_TRACE_LAYER_NAME: &'static core::ffi::CStr =
c"VK_LAYER_NV_GPU_Trace_release_public_2021_4_2";
#[allow(unused)]
const NSIGHT_INTERCEPTION_LAYER_NAME: &'static core::ffi::CStr =
c"VK_LAYER_NV_nomad_release_public_2021_4_2";
pub fn new(
app_name: &str,
instance_layers: &[&CStr],
instance_extensions: &[&CStr],
display_handle: winit::raw_window_handle::DisplayHandle,
) -> Result<Self> {
let entry = unsafe { ash::Entry::load()? };
let app_name = CString::new(app_name)?;
let app_info = vk::ApplicationInfo::default()
.api_version(vk::make_api_version(0, 1, 3, 0))
.application_name(&app_name)
.engine_name(c"PrimalGame")
.application_version(0)
.engine_version(0);
// TODO: make this a flag somewhere to enable or disable validation layers
// DEBUG LAYERS/VALIDATION
let validation_settings = [
vk::LayerSettingEXT::default()
.layer_name(Self::VALIDATION_LAYER_NAME)
.setting_name(c"VK_KHRONOS_VALIDATION_VALIDATE_BEST_PRACTICES")
.ty(vk::LayerSettingTypeEXT::BOOL32)
.values(&[1]),
vk::LayerSettingEXT::default()
.layer_name(Self::VALIDATION_LAYER_NAME)
.setting_name(
c"VK_KHRONOS_VALIDATION_VALIDATE_BEST_PRACTICES_AMD",
)
.ty(vk::LayerSettingTypeEXT::BOOL32)
.values(&[1]),
vk::LayerSettingEXT::default()
.layer_name(Self::VALIDATION_LAYER_NAME)
.setting_name(c"VK_KHRONOS_VALIDATION_VALIDATE_SYNC")
.ty(vk::LayerSettingTypeEXT::BOOL32)
.values(&[1]),
];
let mut validation_info = vk::LayerSettingsCreateInfoEXT::default()
.settings(&validation_settings);
let layers =
Self::get_layers(&entry, &[Self::VALIDATION_LAYER_NAME]).unwrap();
let extensions = Self::get_extensions(
&entry,
&layers,
&[ash::ext::debug_utils::NAME, ash::ext::layer_settings::NAME],
display_handle,
)
.unwrap();
let layers = VkNameList::from_strs(&layers);
let extensions = VkNameList::from_strs(&extensions);
let create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info)
.enabled_extension_names(&extensions.names)
.enabled_layer_names(&layers.names)
.push_next(&mut validation_info);
let instance = unsafe { entry.create_instance(&create_info, None)? };
let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR
| vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
| vk::DebugUtilsMessageSeverityFlagsEXT::INFO,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
)
.pfn_user_callback(Some(debug::debug_callback));
let debug_utils_instance =
ash::ext::debug_utils::Instance::new(&entry, &instance);
let debug_utils_messenger = unsafe {
debug_utils_instance
.create_debug_utils_messenger(&debug_info, None)?
};
let surface_instance =
ash::khr::surface::Instance::new(&entry, &instance);
let instance = Arc::new(Instance {
instance,
debug_utils: debug_utils_instance,
debug_utils_messenger,
surface: surface_instance,
entry,
});
let mut features = PhysicalDeviceFeatures::all_default()
.version(vk::make_api_version(0, 1, 3, 0))
.features10(
vk::PhysicalDeviceFeatures::default()
.sampler_anisotropy(true)
.multi_draw_indirect(true),
)
.features11(
vk::PhysicalDeviceVulkan11Features::default()
.shader_draw_parameters(true),
)
.features12(
vk::PhysicalDeviceVulkan12Features::default()
.shader_int8(true)
.storage_buffer8_bit_access(true),
)
.features13(
vk::PhysicalDeviceVulkan13Features::default()
.dynamic_rendering(true)
.maintenance4(true)
.synchronization2(true),
)
.with_extension(
make_extention_properties(
ash::ext::mesh_shader::NAME,
ash::ext::mesh_shader::SPEC_VERSION,
),
vk::PhysicalDeviceMeshShaderFeaturesEXT::default()
.mesh_shader(true)
.task_shader(true),
)
.with_extension(
make_extention_properties(
ash::ext::index_type_uint8::NAME,
ash::ext::index_type_uint8::SPEC_VERSION,
),
vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default()
.index_type_uint8(true),
)
.with_extensions2([
make_extention_properties(
khr::swapchain::NAME,
khr::swapchain::SPEC_VERSION,
),
make_extention_properties(
khr::spirv_1_4::NAME,
khr::spirv_1_4::SPEC_VERSION,
),
]);
// Consider this: switching physical device in game?
// anything above this point is device agnostic, everything below would have to be recreated
// additionally, pdev would have to be derived from a device and not a scoring function.
let pdev = Self::choose_physical_device(
&instance,
display_handle,
&features,
vec![Box::new(
vk::PhysicalDeviceMeshShaderPropertiesEXT::default(),
)],
)?;
tracing::debug!("pdev: {pdev:?}");
let device =
Arc::new(Self::create_device(&instance, pdev, &mut features)?);
let alloc_info = vk_mem::AllocatorCreateInfo::new(
&instance.instance,
&device.device,
device.physical.pdev,
);
let alloc = Arc::new(unsafe { vk_mem::Allocator::new(alloc_info)? });
Ok(Self {
instance,
device,
alloc,
})
}
fn queue_family_supports_presentation(
instance: &Instance,
pdev: vk::PhysicalDevice,
queue_family: u32,
display_handle: winit::raw_window_handle::DisplayHandle,
) -> bool {
unsafe {
match display_handle.as_raw() {
winit::raw_window_handle::RawDisplayHandle::Xlib(
_xlib_display_handle,
) => {
todo!()
}
winit::raw_window_handle::RawDisplayHandle::Xcb(
_xcb_display_handle,
) => todo!(),
winit::raw_window_handle::RawDisplayHandle::Wayland(
wayland_display_handle,
) => ash::khr::wayland_surface::Instance::new(
&instance.entry,
&instance.instance,
)
.get_physical_device_wayland_presentation_support(
pdev,
queue_family,
wayland_display_handle.display.cast().as_mut(),
),
winit::raw_window_handle::RawDisplayHandle::Drm(_) => {
todo!()
}
winit::raw_window_handle::RawDisplayHandle::Windows(_) => {
ash::khr::win32_surface::Instance::new(
&instance.entry,
&instance.instance,
)
.get_physical_device_win32_presentation_support(
pdev,
queue_family,
)
}
_ => panic!("unsupported platform"),
}
}
}
fn select_pdev_queue_families(
instance: &Instance,
display_handle: winit::raw_window_handle::DisplayHandle,
pdev: vk::PhysicalDevice,
) -> DeviceQueueFamilies {
let queue_families = unsafe {
instance
.instance
.get_physical_device_queue_family_properties(pdev)
};
struct QueueFamily {
num_queues: u32,
is_present: bool,
is_compute: bool,
is_graphics: bool,
is_transfer: bool,
}
impl QueueFamily {
fn is_graphics_and_compute(&self) -> bool {
self.is_compute && self.is_graphics
}
}
struct QueueFamilies(Vec<QueueFamily>);
impl QueueFamilies {
fn find_first<F>(&mut self, mut pred: F) -> Option<u32>
where
F: FnMut(&QueueFamily) -> bool,
{
if let Some((q, family)) = self
.0
.iter_mut()
.enumerate()
.filter(|(_, family)| family.num_queues > 0)
.find(|(_, family)| pred(family))
{
family.num_queues -= 1;
Some(q as u32)
} else {
None
}
}
fn find_best<F>(&mut self, mut pred: F) -> Option<u32>
where
F: FnMut(&QueueFamily) -> Option<u32>,
{
let (_, q, family) = self
.0
.iter_mut()
.enumerate()
.filter_map(|(i, family)| {
if family.num_queues == 0 {
return None;
}
pred(family).map(|score| (score, i, family))
})
.max_by_key(|(score, _, _)| *score)?;
family.num_queues -= 1;
Some(q as u32)
}
}
let mut queue_families = QueueFamilies(
queue_families
.iter()
.enumerate()
.map(|(i, family)| {
let q = i as u32;
let is_graphics =
family.queue_flags.contains(vk::QueueFlags::GRAPHICS);
let is_compute =
family.queue_flags.contains(vk::QueueFlags::COMPUTE);
let is_transfer =
family.queue_flags.contains(vk::QueueFlags::TRANSFER)
|| is_compute
|| is_graphics;
let is_present = Self::queue_family_supports_presentation(
instance,
pdev,
q,
display_handle,
);
QueueFamily {
num_queues: family.queue_count,
is_compute,
is_graphics,
is_present,
is_transfer,
}
})
.collect::<Vec<_>>(),
);
let graphics = queue_families
.find_best(|family| {
if !family.is_graphics {
return None;
}
// a queue with Graphics+Compute is guaranteed to exist
Some(family.is_compute as u32 * 2 + family.is_present as u32)
})
.unwrap();
// find present queue first because it is rather more important than a secondary compute queue
let present =
if !queue_families.0.get(graphics as usize).unwrap().is_present {
// unwrap because we do need a present queue
Some(
queue_families
.find_first(|family| family.is_present)
.unwrap(),
)
} else {
None
};
let async_compute =
queue_families.find_first(|family| family.is_compute);
let transfer = queue_families.find_first(|family| family.is_transfer);
// family of each queue, of which one is allocated for each queue, with graphics being the fallback queue for compute and transfer, and present possibly being `None`, in which case it is Graphics
let queues = DeviceQueueFamilies {
graphics,
async_compute,
transfer,
present: present.or({
if !queue_families.0.get(graphics as usize).unwrap().is_present
{
panic!("no present queue available");
} else {
None
}
}),
};
queues
}
fn create_device(
instance: &Instance,
pdev: PhysicalDevice,
features: &mut PhysicalDeviceFeatures,
) -> Result<Device> {
let mut unique_families = BTreeMap::<u32, u32>::new();
let mut helper = |family: u32| {
use std::collections::btree_map::Entry;
let index = match unique_families.entry(family) {
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(1);
0
}
Entry::Occupied(mut occupied_entry) => {
let idx = occupied_entry.get_mut();
*idx += 1;
*idx - 1
}
};
(family, index)
};
eprintln!("queue families: {:?}", pdev.queue_families);
let graphics_family_and_index = helper(pdev.queue_families.graphics);
let compute_family_and_index =
pdev.queue_families.async_compute.map(|f| helper(f));
let transfer_family_and_index =
pdev.queue_families.transfer.map(|f| helper(f));
let present_family_and_index =
pdev.queue_families.present.map(|f| helper(f));
let priorities = vec![
1.0f32;
unique_families.iter().fold(0, |acc, (_, num)| acc + *num)
as usize
];
eprintln!("unique_families: {unique_families:?}");
let queue_infos = unique_families
.into_iter()
.filter(|&(_, count)| count > 0)
.map(|(family, queues)| {
vk::DeviceQueueCreateInfo::default()
.queue_family_index(family)
.queue_priorities(&priorities[..queues as usize])
})
.collect::<Vec<_>>();
eprintln!("infos: {queue_infos:#?}");
let extensions = features
.device_extensions
.iter()
.map(|ext| ext.extension_name.as_ptr())
.collect::<Vec<_>>();
let mut features2 = features.features2();
let device_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&queue_infos)
.enabled_extension_names(&extensions)
.push_next(&mut features2);
let device = unsafe {
let device = instance.instance.create_device(
pdev.pdev,
&device_info,
None,
)?;
let main_queue = Queue::new(
&device,
graphics_family_and_index.0,
graphics_family_and_index.1,
);
device.get_device_queue(
graphics_family_and_index.0,
graphics_family_and_index.1,
);
let present_queue = present_family_and_index
.map(|(f, i)| Queue::new(&device, f, i))
.unwrap_or(main_queue.clone());
let compute_queue = compute_family_and_index
.map(|(f, i)| Queue::new(&device, f, i))
.unwrap_or(main_queue.clone());
let transfer_queue = transfer_family_and_index
.map(|(f, i)| Queue::new(&device, f, i))
.unwrap_or(compute_queue.clone());
Device {
device: device.clone(),
physical: pdev,
swapchain: khr::swapchain::Device::new(
&instance.instance,
&device,
),
main_queue,
present_queue,
compute_queue,
transfer_queue,
}
};
Ok(device)
}
fn choose_physical_device(
instance: &Instance,
display_handle: winit::raw_window_handle::DisplayHandle,
requirements: &PhysicalDeviceFeatures,
extra_properties: Vec<Box<dyn ExtendsDeviceProperties2Debug>>,
) -> Result<PhysicalDevice> {
let pdevs = unsafe { instance.instance.enumerate_physical_devices()? };
let (pdev, properties) = pdevs
.into_iter()
.map(|pdev| {
let mut props = PhysicalDeviceProperties::default()
.extra_properties(
extra_properties
.iter()
.map(|b| dyn_clone::clone_box(&**b))
.collect::<Vec<_>>(),
);
props.query(&instance.instance, pdev);
(pdev, props)
})
// filter devices which dont support the version of Vulkan we are requesting
.filter(|(_, props)| props.base.api_version >= requirements.version)
// filter devices which don't support the device extensions we
// are requesting
// TODO: figure out a way to fall back to some
// device which doesn't support all of the extensions.
.filter(|(pdev, _)| {
let query_features =
PhysicalDeviceFeatures::query(&instance.instance, *pdev)
.unwrap();
requirements.compatible_with(&query_features)
})
.max_by_key(|(_, props)| {
let score = match props.base.device_type {
vk::PhysicalDeviceType::DISCRETE_GPU => 5,
vk::PhysicalDeviceType::INTEGRATED_GPU => 4,
vk::PhysicalDeviceType::VIRTUAL_GPU => 3,
vk::PhysicalDeviceType::CPU => 2,
vk::PhysicalDeviceType::OTHER => 1,
_ => unreachable!(),
};
// score based on limits or other properties
score
})
.ok_or(Error::NoPhysicalDevice)?;
Ok(PhysicalDevice {
queue_families: Self::select_pdev_queue_families(
instance,
display_handle,
pdev,
),
pdev,
properties,
})
}
fn get_available_extensions(
entry: &ash::Entry,
layers: &[&CStr],
) -> Result<Vec<ash::vk::ExtensionProperties>> {
unsafe {
let extensions = core::iter::once(
entry.enumerate_instance_extension_properties(None),
)
.chain(layers.iter().map(|&layer| {
entry.enumerate_instance_extension_properties(Some(layer))
}))
.filter_map(|result| result.ok())
.flatten()
.collect::<Vec<ash::vk::ExtensionProperties>>();
Ok(extensions)
}
}
fn get_extensions<'a>(
entry: &ash::Entry,
layers: &[&'a CStr],
extensions: &[&'a CStr],
display_handle: winit::raw_window_handle::DisplayHandle,
) -> core::result::Result<Vec<&'a CStr>, (Vec<&'a CStr>, Vec<&'a CStr>)>
{
unsafe {
let available_extensions = Self::get_available_extensions(
entry, layers,
)
.map_err(|_| (Vec::<&'a CStr>::new(), extensions.to_vec()))?;
let available_extension_names = available_extensions
.iter()
.map(|layer| layer.extension_name_as_c_str())
.collect::<core::result::Result<BTreeSet<_>, _>>()
.map_err(|_| (Vec::<&'a CStr>::new(), extensions.to_vec()))?;
let mut out_extensions = Vec::with_capacity(extensions.len());
let mut unsupported_extensions =
Vec::with_capacity(extensions.len());
for &extension in extensions {
if available_extension_names.contains(&extension) {
out_extensions.push(extension);
} else {
unsupported_extensions.push(extension);
}
}
let Ok(required_extension_names) =
ash_window::enumerate_required_extensions(
display_handle.as_raw(),
)
else {
return Err((out_extensions, unsupported_extensions));
};
for &extension in required_extension_names {
let extension = core::ffi::CStr::from_ptr(extension);
if available_extension_names.contains(&extension) {
out_extensions.push(extension);
} else {
unsupported_extensions.push(extension);
}
}
if !unsupported_extensions.is_empty() {
Err((out_extensions, unsupported_extensions))
} else {
Ok(out_extensions)
}
}
}
fn get_layers<'a>(
entry: &ash::Entry,
wants_layers: &[&'a CStr],
) -> core::result::Result<Vec<&'a CStr>, (Vec<&'a CStr>, Vec<&'a CStr>)>
{
unsafe {
let available_layers = entry
.enumerate_instance_layer_properties()
.map_err(|_| (Vec::<&'a CStr>::new(), wants_layers.to_vec()))?;
let available_layer_names = available_layers
.iter()
.map(|layer| layer.layer_name_as_c_str())
.collect::<core::result::Result<BTreeSet<_>, _>>()
.map_err(|_| (Vec::<&'a CStr>::new(), wants_layers.to_vec()))?;
let mut out_layers = Vec::with_capacity(wants_layers.len());
let mut unsupported_layers = Vec::with_capacity(wants_layers.len());
for &layer in wants_layers {
if available_layer_names.contains(&layer) {
out_layers.push(layer);
} else {
unsupported_layers.push(layer);
}
}
if !unsupported_layers.is_empty() {
Err((out_layers, unsupported_layers))
} else {
Ok(out_layers)
}
}
}
}
pub struct WindowContext {
surface: Arc<Surface>,
current_swapchain: Arc<Swapchain>,
}
impl WindowContext {
fn new(
instance: Arc<Instance>,
device: Arc<Device>,
extent: vk::Extent2D,
window: winit::raw_window_handle::RawWindowHandle,
display: winit::raw_window_handle::RawDisplayHandle,
) -> Result<Self> {
let surface =
Arc::new(Surface::create(instance.clone(), display, window)?);
let swapchain = Arc::new(Swapchain::create_new(
instance,
device.clone(),
surface.clone(),
device.physical.pdev,
extent,
)?);
Ok(Self {
surface,
current_swapchain: swapchain,
})
}
pub fn recreate_swapchain(&mut self, extent: vk::Extent2D) -> Result<()> {
self.current_swapchain =
Arc::new(self.current_swapchain.recreate(extent)?);
Ok(())
}
}
pub struct Renderer {
vulkan: Vulkan,
display: winit::raw_window_handle::RawDisplayHandle,
pub window_contexts: HashMap<winit::window::WindowId, WindowContext>,
}
pub use vk::Extent2D;
impl Renderer {
pub fn new(display: DisplayHandle) -> Result<Self> {
let vulkan = Vulkan::new("Vidya", &[], &[], display)?;
Ok(Self {
vulkan,
display: display.as_raw(),
window_contexts: HashMap::new(),
})
}
pub fn debug_draw(&mut self) -> Result<()> {
let dev = Device2(self.vulkan.device.clone());
unsafe { dev.dev().device_wait_idle()? };
let pool = commands::SingleUseCommandPool::new(
dev.clone(),
dev.queue_families().graphics,
)?;
for ctx in self.window_contexts.values() {
let cmd =
commands::SingleUseCommand::new(dev.clone(), pool.pool())?;
let buffer = cmd.command_buffer();
let extent = ctx.current_swapchain.extent;
let ready_semaphore = sync::Semaphore::new(dev.clone())?;
let (swapchain_index, suboptimal) = unsafe {
dev.swapchain().acquire_next_image(
ctx.current_swapchain.swapchain,
u64::MAX,
ready_semaphore.semaphore(),
vk::Fence::null(),
)?
};
if suboptimal {
continue;
}
let image = ctx.current_swapchain.images.first().cloned().unwrap();
// let image = images::Image2D::new_exclusive(
// self.vulkan.alloc.clone(),
// extent,
// 1,
// 1,
// vk::Format::R8G8B8A8_UNORM,
// vk::ImageTiling::OPTIMAL,
// vk::ImageUsageFlags::TRANSFER_SRC,
// vk_mem::MemoryUsage::AutoPreferDevice,
// vk_mem::AllocationCreateFlags::empty(),
// )?;
// let view = image.view(&dev, vk::ImageAspectFlags::COLOR)?;
let clear_values = vk::ClearColorValue {
float32: [0.275, 0.769, 0.941, 1.0],
};
unsafe {
let barriers = [images::image_barrier(
image,
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::empty(),
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
None,
)];
let dependency_info = vk::DependencyInfo::default()
.dependency_flags(vk::DependencyFlags::BY_REGION)
.image_memory_barriers(&barriers);
dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
dev.dev().cmd_clear_color_image(
buffer,
image,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
&clear_values,
&[vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.base_array_layer(0)
.level_count(vk::REMAINING_MIP_LEVELS)
.layer_count(vk::REMAINING_ARRAY_LAYERS)],
);
let barriers = [images::image_barrier(
image,
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::PipelineStageFlags2::BOTTOM_OF_PIPE,
vk::AccessFlags2::empty(),
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::PRESENT_SRC_KHR,
None,
)];
let dependency_info = vk::DependencyInfo::default()
.dependency_flags(vk::DependencyFlags::BY_REGION)
.image_memory_barriers(&barriers);
dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
let signal_semaphore = sync::Semaphore::new(dev.clone())?;
let future = cmd.submit_async(
dev.graphics_queue().clone(),
Some((
ready_semaphore.semaphore(),
vk::PipelineStageFlags::TOP_OF_PIPE,
)),
Some(signal_semaphore.semaphore()),
)?;
let wait_semaphores = [signal_semaphore.semaphore()];
let swapchains = [ctx.current_swapchain.swapchain];
let indices = [swapchain_index];
let present_info = vk::PresentInfoKHR::default()
.image_indices(&indices)
.swapchains(&swapchains)
.wait_semaphores(&wait_semaphores);
dev.present_queue().with_locked(|queue| {
dev.swapchain().queue_present(queue, &present_info)
})?;
future.block_until()?;
}
}
//unsafe {dev.dev().}
Ok(())
}
pub fn new_window_context(
&mut self,
extent: vk::Extent2D,
window_id: winit::window::WindowId,
window: winit::raw_window_handle::WindowHandle,
) -> Result<()> {
use std::collections::hash_map::Entry;
match self.window_contexts.entry(window_id) {
Entry::Vacant(entry) => {
let ctx = WindowContext::new(
self.vulkan.instance.clone(),
self.vulkan.device.clone(),
extent,
window.as_raw(),
self.display,
)?;
entry.insert(ctx);
}
_ => {}
}
Ok(())
}
}
mod debug {
use ash::vk;
use tracing::{event, Level};
unsafe fn str_from_raw_parts<'a>(
str: *const i8,
) -> std::borrow::Cow<'a, str> {
use std::{borrow::Cow, ffi};
if str.is_null() {
Cow::from("")
} else {
ffi::CStr::from_ptr(str).to_string_lossy()
}
}
pub(super) unsafe extern "system" fn debug_callback(
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
message_type: vk::DebugUtilsMessageTypeFlagsEXT,
callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
user_data: *mut core::ffi::c_void,
) -> vk::Bool32 {
_ = user_data;
let callback_data = *callback_data;
let message_id_number = callback_data.message_id_number;
let message_id_name =
str_from_raw_parts(callback_data.p_message_id_name);
let message = str_from_raw_parts(callback_data.p_message);
match message_severity {
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => {
event!(target: "VK::DebugUtils", Level::ERROR, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
}
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => {
event!(target: "VK::DebugUtils", Level::TRACE, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
}
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => {
event!(target: "VK::DebugUtils", Level::INFO, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
}
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => {
event!(target: "VK::DebugUtils", Level::WARN, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
}
_ => unreachable!(),
}
vk::FALSE
}
}
pub mod utils {
#![allow(dead_code)]
use ash::vk;
pub trait SplitCollect: Iterator {
fn collect2<F>(self, mut pred: F) -> (Vec<Self::Item>, Vec<Self::Item>)
where
Self: Sized,
F: FnMut(&Self::Item) -> bool,
{
let mut left = Vec::new();
let mut right = Vec::new();
for item in self {
if pred(&item) {
left.push(item);
} else {
right.push(item);
}
}
(left, right)
}
}
pub fn eq_device_features10(
lhs: &vk::PhysicalDeviceFeatures,
rhs: &vk::PhysicalDeviceFeatures,
) -> bool {
lhs.robust_buffer_access == rhs.robust_buffer_access
&& lhs.full_draw_index_uint32 == rhs.full_draw_index_uint32
&& lhs.image_cube_array == rhs.image_cube_array
&& lhs.independent_blend == rhs.independent_blend
&& lhs.geometry_shader == rhs.geometry_shader
&& lhs.tessellation_shader == rhs.tessellation_shader
&& lhs.sample_rate_shading == rhs.sample_rate_shading
&& lhs.dual_src_blend == rhs.dual_src_blend
&& lhs.logic_op == rhs.logic_op
&& lhs.multi_draw_indirect == rhs.multi_draw_indirect
&& lhs.draw_indirect_first_instance
== rhs.draw_indirect_first_instance
&& lhs.depth_clamp == rhs.depth_clamp
&& lhs.depth_bias_clamp == rhs.depth_bias_clamp
&& lhs.fill_mode_non_solid == rhs.fill_mode_non_solid
&& lhs.depth_bounds == rhs.depth_bounds
&& lhs.wide_lines == rhs.wide_lines
&& lhs.large_points == rhs.large_points
&& lhs.alpha_to_one == rhs.alpha_to_one
&& lhs.multi_viewport == rhs.multi_viewport
&& lhs.sampler_anisotropy == rhs.sampler_anisotropy
&& lhs.texture_compression_etc2 == rhs.texture_compression_etc2
&& lhs.texture_compression_astc_ldr
== rhs.texture_compression_astc_ldr
&& lhs.texture_compression_bc == rhs.texture_compression_bc
&& lhs.occlusion_query_precise == rhs.occlusion_query_precise
&& lhs.pipeline_statistics_query == rhs.pipeline_statistics_query
&& lhs.vertex_pipeline_stores_and_atomics
== rhs.vertex_pipeline_stores_and_atomics
&& lhs.fragment_stores_and_atomics
== rhs.fragment_stores_and_atomics
&& lhs.shader_tessellation_and_geometry_point_size
== rhs.shader_tessellation_and_geometry_point_size
&& lhs.shader_image_gather_extended
== rhs.shader_image_gather_extended
&& lhs.shader_storage_image_extended_formats
== rhs.shader_storage_image_extended_formats
&& lhs.shader_storage_image_multisample
== rhs.shader_storage_image_multisample
&& lhs.shader_storage_image_read_without_format
== rhs.shader_storage_image_read_without_format
&& lhs.shader_storage_image_write_without_format
== rhs.shader_storage_image_write_without_format
&& lhs.shader_uniform_buffer_array_dynamic_indexing
== rhs.shader_uniform_buffer_array_dynamic_indexing
&& lhs.shader_sampled_image_array_dynamic_indexing
== rhs.shader_sampled_image_array_dynamic_indexing
&& lhs.shader_storage_buffer_array_dynamic_indexing
== rhs.shader_storage_buffer_array_dynamic_indexing
&& lhs.shader_storage_image_array_dynamic_indexing
== rhs.shader_storage_image_array_dynamic_indexing
&& lhs.shader_clip_distance == rhs.shader_clip_distance
&& lhs.shader_cull_distance == rhs.shader_cull_distance
&& lhs.shader_float64 == rhs.shader_float64
&& lhs.shader_int64 == rhs.shader_int64
&& lhs.shader_int16 == rhs.shader_int16
&& lhs.shader_resource_residency == rhs.shader_resource_residency
&& lhs.shader_resource_min_lod == rhs.shader_resource_min_lod
&& lhs.sparse_binding == rhs.sparse_binding
&& lhs.sparse_residency_buffer == rhs.sparse_residency_buffer
&& lhs.sparse_residency_image2_d == rhs.sparse_residency_image2_d
&& lhs.sparse_residency_image3_d == rhs.sparse_residency_image3_d
&& lhs.sparse_residency2_samples == rhs.sparse_residency2_samples
&& lhs.sparse_residency4_samples == rhs.sparse_residency4_samples
&& lhs.sparse_residency8_samples == rhs.sparse_residency8_samples
&& lhs.sparse_residency16_samples == rhs.sparse_residency16_samples
&& lhs.sparse_residency_aliased == rhs.sparse_residency_aliased
&& lhs.variable_multisample_rate == rhs.variable_multisample_rate
&& lhs.inherited_queries == rhs.inherited_queries
}
pub fn eq_device_features11(
lhs: &vk::PhysicalDeviceVulkan11Features,
rhs: &vk::PhysicalDeviceVulkan11Features,
) -> bool {
lhs.storage_buffer16_bit_access == rhs.storage_buffer16_bit_access
&& lhs.uniform_and_storage_buffer16_bit_access
== rhs.uniform_and_storage_buffer16_bit_access
&& lhs.storage_push_constant16 == rhs.storage_push_constant16
&& lhs.storage_input_output16 == rhs.storage_input_output16
&& lhs.multiview == rhs.multiview
&& lhs.multiview_geometry_shader == rhs.multiview_geometry_shader
&& lhs.multiview_tessellation_shader
== rhs.multiview_tessellation_shader
&& lhs.variable_pointers_storage_buffer
== rhs.variable_pointers_storage_buffer
&& lhs.variable_pointers == rhs.variable_pointers
&& lhs.protected_memory == rhs.protected_memory
&& lhs.sampler_ycbcr_conversion == rhs.sampler_ycbcr_conversion
&& lhs.shader_draw_parameters == rhs.shader_draw_parameters
}
pub fn eq_device_features12(
lhs: &vk::PhysicalDeviceVulkan12Features,
rhs: &vk::PhysicalDeviceVulkan12Features,
) -> bool {
lhs.sampler_mirror_clamp_to_edge == rhs.sampler_mirror_clamp_to_edge
&& lhs.draw_indirect_count == rhs.draw_indirect_count
&& lhs.storage_buffer8_bit_access == rhs.storage_buffer8_bit_access
&& lhs.uniform_and_storage_buffer8_bit_access
== rhs.uniform_and_storage_buffer8_bit_access
&& lhs.storage_push_constant8 == rhs.storage_push_constant8
&& lhs.shader_buffer_int64_atomics
== rhs.shader_buffer_int64_atomics
&& lhs.shader_shared_int64_atomics
== rhs.shader_shared_int64_atomics
&& lhs.shader_float16 == rhs.shader_float16
&& lhs.shader_int8 == rhs.shader_int8
&& lhs.descriptor_indexing == rhs.descriptor_indexing
&& lhs.shader_input_attachment_array_dynamic_indexing
== rhs.shader_input_attachment_array_dynamic_indexing
&& lhs.shader_uniform_texel_buffer_array_dynamic_indexing
== rhs.shader_uniform_texel_buffer_array_dynamic_indexing
&& lhs.shader_storage_texel_buffer_array_dynamic_indexing
== rhs.shader_storage_texel_buffer_array_dynamic_indexing
&& lhs.shader_uniform_buffer_array_non_uniform_indexing
== rhs.shader_uniform_buffer_array_non_uniform_indexing
&& lhs.shader_sampled_image_array_non_uniform_indexing
== rhs.shader_sampled_image_array_non_uniform_indexing
&& lhs.shader_storage_buffer_array_non_uniform_indexing
== rhs.shader_storage_buffer_array_non_uniform_indexing
&& lhs.shader_storage_image_array_non_uniform_indexing
== rhs.shader_storage_image_array_non_uniform_indexing
&& lhs.shader_input_attachment_array_non_uniform_indexing
== rhs.shader_input_attachment_array_non_uniform_indexing
&& lhs.shader_uniform_texel_buffer_array_non_uniform_indexing
== rhs.shader_uniform_texel_buffer_array_non_uniform_indexing
&& lhs.shader_storage_texel_buffer_array_non_uniform_indexing
== rhs.shader_storage_texel_buffer_array_non_uniform_indexing
&& lhs.descriptor_binding_uniform_buffer_update_after_bind
== rhs.descriptor_binding_uniform_buffer_update_after_bind
&& lhs.descriptor_binding_sampled_image_update_after_bind
== rhs.descriptor_binding_sampled_image_update_after_bind
&& lhs.descriptor_binding_storage_image_update_after_bind
== rhs.descriptor_binding_storage_image_update_after_bind
&& lhs.descriptor_binding_storage_buffer_update_after_bind
== rhs.descriptor_binding_storage_buffer_update_after_bind
&& lhs.descriptor_binding_uniform_texel_buffer_update_after_bind
== rhs.descriptor_binding_uniform_texel_buffer_update_after_bind
&& lhs.descriptor_binding_storage_texel_buffer_update_after_bind
== rhs.descriptor_binding_storage_texel_buffer_update_after_bind
&& lhs.descriptor_binding_update_unused_while_pending
== rhs.descriptor_binding_update_unused_while_pending
&& lhs.descriptor_binding_partially_bound
== rhs.descriptor_binding_partially_bound
&& lhs.descriptor_binding_variable_descriptor_count
== rhs.descriptor_binding_variable_descriptor_count
&& lhs.runtime_descriptor_array == rhs.runtime_descriptor_array
&& lhs.sampler_filter_minmax == rhs.sampler_filter_minmax
&& lhs.scalar_block_layout == rhs.scalar_block_layout
&& lhs.imageless_framebuffer == rhs.imageless_framebuffer
&& lhs.uniform_buffer_standard_layout
== rhs.uniform_buffer_standard_layout
&& lhs.shader_subgroup_extended_types
== rhs.shader_subgroup_extended_types
&& lhs.separate_depth_stencil_layouts
== rhs.separate_depth_stencil_layouts
&& lhs.host_query_reset == rhs.host_query_reset
&& lhs.timeline_semaphore == rhs.timeline_semaphore
&& lhs.buffer_device_address == rhs.buffer_device_address
&& lhs.buffer_device_address_capture_replay
== rhs.buffer_device_address_capture_replay
&& lhs.buffer_device_address_multi_device
== rhs.buffer_device_address_multi_device
&& lhs.vulkan_memory_model == rhs.vulkan_memory_model
&& lhs.vulkan_memory_model_device_scope
== rhs.vulkan_memory_model_device_scope
&& lhs.vulkan_memory_model_availability_visibility_chains
== rhs.vulkan_memory_model_availability_visibility_chains
&& lhs.shader_output_viewport_index
== rhs.shader_output_viewport_index
&& lhs.shader_output_layer == rhs.shader_output_layer
&& lhs.subgroup_broadcast_dynamic_id
== rhs.subgroup_broadcast_dynamic_id
}
pub fn eq_device_features13(
lhs: &vk::PhysicalDeviceVulkan13Features,
rhs: &vk::PhysicalDeviceVulkan13Features,
) -> bool {
lhs.robust_image_access == rhs.robust_image_access
&& lhs.inline_uniform_block == rhs.inline_uniform_block
&& lhs.descriptor_binding_inline_uniform_block_update_after_bind
== rhs.descriptor_binding_inline_uniform_block_update_after_bind
&& lhs.pipeline_creation_cache_control
== rhs.pipeline_creation_cache_control
&& lhs.private_data == rhs.private_data
&& lhs.shader_demote_to_helper_invocation
== rhs.shader_demote_to_helper_invocation
&& lhs.shader_terminate_invocation
== rhs.shader_terminate_invocation
&& lhs.subgroup_size_control == rhs.subgroup_size_control
&& lhs.compute_full_subgroups == rhs.compute_full_subgroups
&& lhs.synchronization2 == rhs.synchronization2
&& lhs.texture_compression_astc_hdr
== rhs.texture_compression_astc_hdr
&& lhs.shader_zero_initialize_workgroup_memory
== rhs.shader_zero_initialize_workgroup_memory
&& lhs.dynamic_rendering == rhs.dynamic_rendering
&& lhs.shader_integer_dot_product == rhs.shader_integer_dot_product
&& lhs.maintenance4 == rhs.maintenance4
}
pub fn bitand_device_features10(
lhs: &vk::PhysicalDeviceFeatures,
rhs: &vk::PhysicalDeviceFeatures,
) -> vk::PhysicalDeviceFeatures {
use core::ops::BitAnd;
vk::PhysicalDeviceFeatures {
robust_buffer_access: lhs
.robust_buffer_access
.bitand(&rhs.robust_buffer_access),
full_draw_index_uint32: lhs
.full_draw_index_uint32
.bitand(&rhs.full_draw_index_uint32),
image_cube_array: lhs
.image_cube_array
.bitand(&rhs.image_cube_array),
independent_blend: lhs
.independent_blend
.bitand(&rhs.independent_blend),
geometry_shader: lhs.geometry_shader.bitand(&rhs.geometry_shader),
tessellation_shader: lhs
.tessellation_shader
.bitand(&rhs.tessellation_shader),
sample_rate_shading: lhs
.sample_rate_shading
.bitand(&rhs.sample_rate_shading),
dual_src_blend: lhs.dual_src_blend.bitand(&rhs.dual_src_blend),
logic_op: lhs.logic_op.bitand(&rhs.logic_op),
multi_draw_indirect: lhs
.multi_draw_indirect
.bitand(&rhs.multi_draw_indirect),
draw_indirect_first_instance: lhs
.draw_indirect_first_instance
.bitand(&rhs.draw_indirect_first_instance),
depth_clamp: lhs.depth_clamp.bitand(&rhs.depth_clamp),
depth_bias_clamp: lhs
.depth_bias_clamp
.bitand(&rhs.depth_bias_clamp),
fill_mode_non_solid: lhs
.fill_mode_non_solid
.bitand(&rhs.fill_mode_non_solid),
depth_bounds: lhs.depth_bounds.bitand(&rhs.depth_bounds),
wide_lines: lhs.wide_lines.bitand(&rhs.wide_lines),
large_points: lhs.large_points.bitand(&rhs.large_points),
alpha_to_one: lhs.alpha_to_one.bitand(&rhs.alpha_to_one),
multi_viewport: lhs.multi_viewport.bitand(&rhs.multi_viewport),
sampler_anisotropy: lhs
.sampler_anisotropy
.bitand(&rhs.sampler_anisotropy),
texture_compression_etc2: lhs
.texture_compression_etc2
.bitand(&rhs.texture_compression_etc2),
texture_compression_astc_ldr: lhs
.texture_compression_astc_ldr
.bitand(&rhs.texture_compression_astc_ldr),
texture_compression_bc: lhs
.texture_compression_bc
.bitand(&rhs.texture_compression_bc),
occlusion_query_precise: lhs
.occlusion_query_precise
.bitand(&rhs.occlusion_query_precise),
pipeline_statistics_query: lhs
.pipeline_statistics_query
.bitand(&rhs.pipeline_statistics_query),
vertex_pipeline_stores_and_atomics: lhs
.vertex_pipeline_stores_and_atomics
.bitand(&rhs.vertex_pipeline_stores_and_atomics),
fragment_stores_and_atomics: lhs
.fragment_stores_and_atomics
.bitand(&rhs.fragment_stores_and_atomics),
shader_tessellation_and_geometry_point_size: lhs
.shader_tessellation_and_geometry_point_size
.bitand(&rhs.shader_tessellation_and_geometry_point_size),
shader_image_gather_extended: lhs
.shader_image_gather_extended
.bitand(&rhs.shader_image_gather_extended),
shader_storage_image_extended_formats: lhs
.shader_storage_image_extended_formats
.bitand(&rhs.shader_storage_image_extended_formats),
shader_storage_image_multisample: lhs
.shader_storage_image_multisample
.bitand(&rhs.shader_storage_image_multisample),
shader_storage_image_read_without_format: lhs
.shader_storage_image_read_without_format
.bitand(&rhs.shader_storage_image_read_without_format),
shader_storage_image_write_without_format: lhs
.shader_storage_image_write_without_format
.bitand(&rhs.shader_storage_image_write_without_format),
shader_uniform_buffer_array_dynamic_indexing: lhs
.shader_uniform_buffer_array_dynamic_indexing
.bitand(&rhs.shader_uniform_buffer_array_dynamic_indexing),
shader_sampled_image_array_dynamic_indexing: lhs
.shader_sampled_image_array_dynamic_indexing
.bitand(&rhs.shader_sampled_image_array_dynamic_indexing),
shader_storage_buffer_array_dynamic_indexing: lhs
.shader_storage_buffer_array_dynamic_indexing
.bitand(&rhs.shader_storage_buffer_array_dynamic_indexing),
shader_storage_image_array_dynamic_indexing: lhs
.shader_storage_image_array_dynamic_indexing
.bitand(&rhs.shader_storage_image_array_dynamic_indexing),
shader_clip_distance: lhs
.shader_clip_distance
.bitand(&rhs.shader_clip_distance),
shader_cull_distance: lhs
.shader_cull_distance
.bitand(&rhs.shader_cull_distance),
shader_float64: lhs.shader_float64.bitand(&rhs.shader_float64),
shader_int64: lhs.shader_int64.bitand(&rhs.shader_int64),
shader_int16: lhs.shader_int16.bitand(&rhs.shader_int16),
shader_resource_residency: lhs
.shader_resource_residency
.bitand(&rhs.shader_resource_residency),
shader_resource_min_lod: lhs
.shader_resource_min_lod
.bitand(&rhs.shader_resource_min_lod),
sparse_binding: lhs.sparse_binding.bitand(&rhs.sparse_binding),
sparse_residency_buffer: lhs
.sparse_residency_buffer
.bitand(&rhs.sparse_residency_buffer),
sparse_residency_image2_d: lhs
.sparse_residency_image2_d
.bitand(&rhs.sparse_residency_image2_d),
sparse_residency_image3_d: lhs
.sparse_residency_image3_d
.bitand(&rhs.sparse_residency_image3_d),
sparse_residency2_samples: lhs
.sparse_residency2_samples
.bitand(&rhs.sparse_residency2_samples),
sparse_residency4_samples: lhs
.sparse_residency4_samples
.bitand(&rhs.sparse_residency4_samples),
sparse_residency8_samples: lhs
.sparse_residency8_samples
.bitand(&rhs.sparse_residency8_samples),
sparse_residency16_samples: lhs
.sparse_residency16_samples
.bitand(&rhs.sparse_residency16_samples),
sparse_residency_aliased: lhs
.sparse_residency_aliased
.bitand(&rhs.sparse_residency_aliased),
variable_multisample_rate: lhs
.variable_multisample_rate
.bitand(&rhs.variable_multisample_rate),
inherited_queries: lhs
.inherited_queries
.bitand(&rhs.inherited_queries),
}
}
pub fn bitand_device_features11(
lhs: &vk::PhysicalDeviceVulkan11Features,
rhs: &vk::PhysicalDeviceVulkan11Features,
) -> vk::PhysicalDeviceVulkan11Features<'static> {
use core::ops::BitAnd;
vk::PhysicalDeviceVulkan11Features {
storage_buffer16_bit_access: lhs
.storage_buffer16_bit_access
.bitand(&rhs.storage_buffer16_bit_access),
uniform_and_storage_buffer16_bit_access: lhs
.uniform_and_storage_buffer16_bit_access
.bitand(&rhs.uniform_and_storage_buffer16_bit_access),
storage_push_constant16: lhs
.storage_push_constant16
.bitand(&rhs.storage_push_constant16),
storage_input_output16: lhs
.storage_input_output16
.bitand(&rhs.storage_input_output16),
multiview: lhs.multiview.bitand(&rhs.multiview),
multiview_geometry_shader: lhs
.multiview_geometry_shader
.bitand(&rhs.multiview_geometry_shader),
multiview_tessellation_shader: lhs
.multiview_tessellation_shader
.bitand(&rhs.multiview_tessellation_shader),
variable_pointers_storage_buffer: lhs
.variable_pointers_storage_buffer
.bitand(&rhs.variable_pointers_storage_buffer),
variable_pointers: lhs
.variable_pointers
.bitand(&rhs.variable_pointers),
protected_memory: lhs
.protected_memory
.bitand(&rhs.protected_memory),
sampler_ycbcr_conversion: lhs
.sampler_ycbcr_conversion
.bitand(&rhs.sampler_ycbcr_conversion),
shader_draw_parameters: lhs
.shader_draw_parameters
.bitand(&rhs.shader_draw_parameters),
..Default::default()
}
}
pub fn bitand_device_features12(
lhs: &vk::PhysicalDeviceVulkan12Features,
rhs: &vk::PhysicalDeviceVulkan12Features,
) -> vk::PhysicalDeviceVulkan12Features<'static> {
use core::ops::BitAnd;
vk::PhysicalDeviceVulkan12Features {
sampler_mirror_clamp_to_edge: lhs
.sampler_mirror_clamp_to_edge
.bitand(&rhs.sampler_mirror_clamp_to_edge),
draw_indirect_count: lhs.draw_indirect_count.bitand(&rhs.draw_indirect_count),
storage_buffer8_bit_access: lhs
.storage_buffer8_bit_access
.bitand(&rhs.storage_buffer8_bit_access),
uniform_and_storage_buffer8_bit_access: lhs
.uniform_and_storage_buffer8_bit_access
.bitand(&rhs.uniform_and_storage_buffer8_bit_access),
storage_push_constant8: lhs
.storage_push_constant8
.bitand(&rhs.storage_push_constant8),
shader_buffer_int64_atomics: lhs
.shader_buffer_int64_atomics
.bitand(&rhs.shader_buffer_int64_atomics),
shader_shared_int64_atomics: lhs
.shader_shared_int64_atomics
.bitand(&rhs.shader_shared_int64_atomics),
shader_float16: lhs.shader_float16.bitand(&rhs.shader_float16),
shader_int8: lhs.shader_int8.bitand(&rhs.shader_int8),
descriptor_indexing: lhs.descriptor_indexing.bitand(&rhs.descriptor_indexing),
shader_input_attachment_array_dynamic_indexing: lhs
.shader_input_attachment_array_dynamic_indexing
.bitand(&rhs.shader_input_attachment_array_dynamic_indexing),
shader_uniform_texel_buffer_array_dynamic_indexing: lhs
.shader_uniform_texel_buffer_array_dynamic_indexing
.bitand(&rhs.shader_uniform_texel_buffer_array_dynamic_indexing),
shader_storage_texel_buffer_array_dynamic_indexing: lhs
.shader_storage_texel_buffer_array_dynamic_indexing
.bitand(&rhs.shader_storage_texel_buffer_array_dynamic_indexing),
shader_uniform_buffer_array_non_uniform_indexing: lhs
.shader_uniform_buffer_array_non_uniform_indexing
.bitand(&rhs.shader_uniform_buffer_array_non_uniform_indexing),
shader_sampled_image_array_non_uniform_indexing: lhs
.shader_sampled_image_array_non_uniform_indexing
.bitand(&rhs.shader_sampled_image_array_non_uniform_indexing),
shader_storage_buffer_array_non_uniform_indexing: lhs
.shader_storage_buffer_array_non_uniform_indexing
.bitand(&rhs.shader_storage_buffer_array_non_uniform_indexing),
shader_storage_image_array_non_uniform_indexing: lhs
.shader_storage_image_array_non_uniform_indexing
.bitand(&rhs.shader_storage_image_array_non_uniform_indexing),
shader_input_attachment_array_non_uniform_indexing: lhs
.shader_input_attachment_array_non_uniform_indexing
.bitand(&rhs.shader_input_attachment_array_non_uniform_indexing),
shader_uniform_texel_buffer_array_non_uniform_indexing: lhs
.shader_uniform_texel_buffer_array_non_uniform_indexing
.bitand(&rhs.shader_uniform_texel_buffer_array_non_uniform_indexing),
shader_storage_texel_buffer_array_non_uniform_indexing: lhs
.shader_storage_texel_buffer_array_non_uniform_indexing
.bitand(&rhs.shader_storage_texel_buffer_array_non_uniform_indexing),
descriptor_binding_uniform_buffer_update_after_bind: lhs
.descriptor_binding_uniform_buffer_update_after_bind
.bitand(&rhs.descriptor_binding_uniform_buffer_update_after_bind),
descriptor_binding_sampled_image_update_after_bind: lhs
.descriptor_binding_sampled_image_update_after_bind
.bitand(&rhs.descriptor_binding_sampled_image_update_after_bind),
descriptor_binding_storage_image_update_after_bind: lhs
.descriptor_binding_storage_image_update_after_bind
.bitand(&rhs.descriptor_binding_storage_image_update_after_bind),
descriptor_binding_storage_buffer_update_after_bind: lhs
.descriptor_binding_storage_buffer_update_after_bind
.bitand(&rhs.descriptor_binding_storage_buffer_update_after_bind),
descriptor_binding_uniform_texel_buffer_update_after_bind: lhs
.descriptor_binding_uniform_texel_buffer_update_after_bind
.bitand(&rhs.descriptor_binding_uniform_texel_buffer_update_after_bind),
descriptor_binding_storage_texel_buffer_update_after_bind: lhs
.descriptor_binding_storage_texel_buffer_update_after_bind
.bitand(&rhs.descriptor_binding_storage_texel_buffer_update_after_bind),
descriptor_binding_update_unused_while_pending: lhs
.descriptor_binding_update_unused_while_pending
.bitand(&rhs.descriptor_binding_update_unused_while_pending),
descriptor_binding_partially_bound: lhs
.descriptor_binding_partially_bound
.bitand(&rhs.descriptor_binding_partially_bound),
descriptor_binding_variable_descriptor_count: lhs
.descriptor_binding_variable_descriptor_count
.bitand(&rhs.descriptor_binding_variable_descriptor_count),
runtime_descriptor_array: lhs
.runtime_descriptor_array
.bitand(&rhs.runtime_descriptor_array),
sampler_filter_minmax: lhs.sampler_filter_minmax.bitand(&rhs.sampler_filter_minmax),
scalar_block_layout: lhs.scalar_block_layout.bitand(&rhs.scalar_block_layout),
imageless_framebuffer: lhs.imageless_framebuffer.bitand(&rhs.imageless_framebuffer),
uniform_buffer_standard_layout: lhs
.uniform_buffer_standard_layout
.bitand(&rhs.uniform_buffer_standard_layout),
shader_subgroup_extended_types: lhs
.shader_subgroup_extended_types
.bitand(&rhs.shader_subgroup_extended_types),
separate_depth_stencil_layouts: lhs
.separate_depth_stencil_layouts
.bitand(&rhs.separate_depth_stencil_layouts),
host_query_reset: lhs.host_query_reset.bitand(&rhs.host_query_reset),
timeline_semaphore: lhs.timeline_semaphore.bitand(&rhs.timeline_semaphore),
buffer_device_address: lhs.buffer_device_address.bitand(&rhs.buffer_device_address),
buffer_device_address_capture_replay: lhs
.buffer_device_address_capture_replay
.bitand(&rhs.buffer_device_address_capture_replay),
buffer_device_address_multi_device: lhs
.buffer_device_address_multi_device
.bitand(&rhs.buffer_device_address_multi_device),
vulkan_memory_model: lhs.vulkan_memory_model.bitand(&rhs.vulkan_memory_model),
vulkan_memory_model_device_scope: lhs
.vulkan_memory_model_device_scope
.bitand(&rhs.vulkan_memory_model_device_scope),
vulkan_memory_model_availability_visibility_chains: lhs
.vulkan_memory_model_availability_visibility_chains
.bitand(&rhs.vulkan_memory_model_availability_visibility_chains),
shader_output_viewport_index: lhs
.shader_output_viewport_index
.bitand(&rhs.shader_output_viewport_index),
shader_output_layer: lhs.shader_output_layer.bitand(&rhs.shader_output_layer),
subgroup_broadcast_dynamic_id: lhs
.subgroup_broadcast_dynamic_id
.bitand(&rhs.subgroup_broadcast_dynamic_id),
..Default::default()
}
}
pub fn bitand_device_features13(
lhs: &vk::PhysicalDeviceVulkan13Features,
rhs: &vk::PhysicalDeviceVulkan13Features,
) -> vk::PhysicalDeviceVulkan13Features<'static> {
use core::ops::BitAnd;
vk::PhysicalDeviceVulkan13Features {
robust_image_access: lhs.robust_image_access.bitand(&rhs.robust_image_access),
inline_uniform_block: lhs.inline_uniform_block.bitand(&rhs.inline_uniform_block),
descriptor_binding_inline_uniform_block_update_after_bind: lhs
.descriptor_binding_inline_uniform_block_update_after_bind
.bitand(&rhs.descriptor_binding_inline_uniform_block_update_after_bind),
pipeline_creation_cache_control: lhs
.pipeline_creation_cache_control
.bitand(&rhs.pipeline_creation_cache_control),
private_data: lhs.private_data.bitand(&rhs.private_data),
shader_demote_to_helper_invocation: lhs
.shader_demote_to_helper_invocation
.bitand(&rhs.shader_demote_to_helper_invocation),
shader_terminate_invocation: lhs
.shader_terminate_invocation
.bitand(&rhs.shader_terminate_invocation),
subgroup_size_control: lhs.subgroup_size_control.bitand(&rhs.subgroup_size_control),
compute_full_subgroups: lhs
.compute_full_subgroups
.bitand(&rhs.compute_full_subgroups),
synchronization2: lhs.synchronization2.bitand(&rhs.synchronization2),
texture_compression_astc_hdr: lhs
.texture_compression_astc_hdr
.bitand(&rhs.texture_compression_astc_hdr),
shader_zero_initialize_workgroup_memory: lhs
.shader_zero_initialize_workgroup_memory
.bitand(&rhs.shader_zero_initialize_workgroup_memory),
dynamic_rendering: lhs.dynamic_rendering.bitand(&rhs.dynamic_rendering),
shader_integer_dot_product: lhs
.shader_integer_dot_product
.bitand(&rhs.shader_integer_dot_product),
maintenance4: lhs.maintenance4.bitand(&rhs.maintenance4),
..Default::default()
}
}
}