diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index e36006e..310a1b9 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,25 +1,86 @@ +use std::sync::Arc; + +use bevy_ecs::resource::Resource; use engine_window::raw_handle::WindowWrapper; -use vulkan_context::VulkanContext; +use utils::{device::create_and_insert_device, instance::create_and_insert_instance}; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, DeviceExtensions, DeviceFeatures, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; use window_render_context::WindowRenderContext; use bevy_app::{App, Plugin}; mod utils; -mod vulkan_context; mod window_render_context; +#[derive(Resource)] +pub struct VulkanInstance(Arc); + +#[derive(Resource)] +pub struct VulkanDevice(Arc); + +#[derive(Resource)] +pub struct VulkanGraphicsQueue(Arc); + +#[derive(Resource)] +pub struct VulkanComputeQueue(Arc); + +#[derive(Resource)] +pub struct VulkanTransferQueue(Arc); +#[derive(Resource)] +pub struct VulkanMemoryAllocator(Arc); + +#[derive(Resource)] +pub struct VulkanCommandBufferAllocator(Arc); + +#[derive(Resource)] +pub struct VulkanDescriptorSetAllocator(Arc); + #[derive(Debug, thiserror::Error)] pub enum VulkanError { #[error("Failed to create vulkan context")] FailedToCreateVulkanContext, } -pub struct VulkanPlugin; +pub struct VulkanConfig { + pub instance_layers: Vec, + pub device_extensions: DeviceExtensions, + pub device_features: DeviceFeatures, + pub with_window_surface: bool, + pub with_graphics_queue: bool, + pub with_compute_queue: bool, + pub with_transfer_queue: bool, +} + +impl Default for VulkanConfig { + fn default() -> Self { + Self { + instance_layers: Vec::default(), + device_extensions: DeviceExtensions::default(), + device_features: DeviceFeatures::default(), + with_window_surface: true, + with_graphics_queue: true, + with_compute_queue: true, + with_transfer_queue: true, + } + } +} + +#[derive(Default)] +pub struct VulkanPlugin { + pub vulkan_config: VulkanConfig, +} impl Plugin for VulkanPlugin { fn build(&self, app: &mut App) { - let vulkan_context = VulkanContext::from(app as &App); - app.world_mut().insert_resource(vulkan_context); + let world = app.world_mut(); + + create_and_insert_instance(world, &self.vulkan_config); + create_and_insert_device(world, &self.vulkan_config); } fn ready(&self, app: &App) -> bool { diff --git a/crates/engine_vulkan/src/utils.rs b/crates/engine_vulkan/src/utils.rs deleted file mode 100644 index 046528f..0000000 --- a/crates/engine_vulkan/src/utils.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::sync::Arc; - -use vulkano::{ - Version, VulkanLibrary, - device::{ - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, - QueueFlags, - physical::{PhysicalDevice, PhysicalDeviceType}, - }, - instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, -}; -use winit::raw_window_handle::HasDisplayHandle; - -pub(super) fn load_library() -> Arc { - let library = VulkanLibrary::new().unwrap(); - - log::debug!("Available layer:"); - for layer in library.layer_properties().unwrap() { - log::debug!( - "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", - layer.name(), - layer.description(), - layer.implementation_version(), - layer.vulkan_version() - ); - } - - library -} - -pub(super) fn create_instance( - library: Arc, - required_extensions: InstanceExtensions, -) -> Arc { - Instance::new( - library, - InstanceCreateInfo { - // Enable enumerating devices that use non-conformant Vulkan implementations. - // (e.g. MoltenVK) - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], - ..Default::default() - }, - ) - .unwrap() -} - -pub(super) fn find_physical_device_queue_family_indexes( - physical_device: &Arc, - display_handle: &impl HasDisplayHandle, -) -> Option { - let mut graphic_queue_family_index = None; - - for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { - if queue_family_property - .queue_flags - .intersects(QueueFlags::GRAPHICS) - && physical_device - .presentation_support(i as u32, display_handle) - .unwrap() - { - graphic_queue_family_index = Some(i as u32); - } - } - - graphic_queue_family_index -} - -pub(super) fn pick_physical_device_and_queue_family_indexes( - instance: &Arc, - display_handle: &impl HasDisplayHandle, - device_extensions: &DeviceExtensions, -) -> Option<(Arc, u32)> { - instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) - .filter(|p| p.supported_extensions().contains(device_extensions)) - .filter_map(|p| { - find_physical_device_queue_family_indexes(&p, display_handle) - .and_then(|indexes| Some((p, indexes))) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) -} - -pub(super) fn pick_graphics_device( - instance: &Arc, - display_handle: &impl HasDisplayHandle, -) -> (Arc, impl ExactSizeIterator>) { - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, graphics_family_index) = - pick_physical_device_and_queue_family_indexes(instance, display_handle, &device_extensions) - .unwrap(); - - log::debug!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - if physical_device.api_version() < Version::V1_3 { - device_extensions.khr_dynamic_rendering = true; - } - - log::debug!("Using device extensions: {:#?}", device_extensions); - - Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index: graphics_family_index, - ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - ..Default::default() - }, - ) - .unwrap() -} diff --git a/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/utils/device.rs new file mode 100644 index 0000000..6c4b49f --- /dev/null +++ b/crates/engine_vulkan/src/utils/device.rs @@ -0,0 +1,301 @@ +use std::sync::Arc; + +use bevy_ecs::world::World; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, + }, + memory::allocator::StandardMemoryAllocator, +}; + +use crate::{ + VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanConfig, VulkanDescriptorSetAllocator, + VulkanDevice, VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator, VulkanTransferQueue, +}; + +pub fn create_and_insert_device(world: &mut World, config: &VulkanConfig) { + let picked_device = + pick_physical_device(world, &config).expect("Failed to pick physical device"); + + let device = picked_device.device; + let physical_device = device.physical_device(); + + log::debug!("Vulkan device created"); + log::debug!( + "\tPhysical device: {:?} ({:?})", + physical_device.properties().device_name, + physical_device.properties().device_type + ); + log::debug!("\tDevice extensions: {:?}", device.enabled_extensions()); + log::debug!("\tDevice features: {:?}", device.enabled_features()); + + world.insert_resource(VulkanDevice(device.clone())); + + log::debug!("\tDevice selected queues:"); + if config.with_graphics_queue { + world.insert_resource(VulkanGraphicsQueue( + picked_device + .graphics_queue + .expect("Failed to get graphics queue"), + )); + log::debug!("\t\t- Graphics queue"); + } + + if config.with_compute_queue { + world.insert_resource(VulkanComputeQueue( + picked_device + .compute_queue + .expect("Failed to get compute queue"), + )); + log::debug!("\t\t- Compute queue"); + } + + if config.with_transfer_queue { + world.insert_resource(VulkanTransferQueue( + picked_device + .transfer_queue + .expect("Failed to get transfer queue"), + )); + log::debug!("\t\t- Transfer queue"); + } + + world.insert_resource(VulkanMemoryAllocator(Arc::new( + StandardMemoryAllocator::new_default(device.clone()), + ))); + world.insert_resource(VulkanCommandBufferAllocator(Arc::new( + StandardCommandBufferAllocator::new(device.clone(), Default::default()), + ))); + world.insert_resource(VulkanDescriptorSetAllocator(Arc::new( + StandardDescriptorSetAllocator::new(device.clone(), Default::default()), + ))); +} + +struct PickedDevice { + pub device: Arc, + pub graphics_queue: Option>, + pub compute_queue: Option>, + pub transfer_queue: Option>, +} + +fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option { + let instance = world + .get_resource::() + .expect("Failed to get VulkanInstance during vulkan plugin initialization"); + + instance + .0 + .enumerate_physical_devices() + .expect("Failed to enumerate physical devices") + .filter_map(|p| check_physical_device(world, &p, config)) + .min_by_key( + |p| match p.device.physical_device().properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }, + ) + .take() +} + +fn check_device_extensions_support( + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + let device_extensions = DeviceExtensions { + khr_swapchain: config.with_window_surface, + ..config.device_extensions + }; + + if physical_device + .supported_extensions() + .contains(&device_extensions) + { + log::debug!( + "\t\t[OK] Device supports required extensions {:?}", + device_extensions + ); + Some(device_extensions) + } else { + log::debug!( + "\t\t[FAILED] Device does not support required extensions {:?}", + device_extensions + ); + None + } +} + +struct PickedQueuesInfo { + graphics_queue_family_index: Option, + compute_queue_family_index: Option, + transfer_queue_family_index: Option, +} + +fn check_queues_support( + world: &World, + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + let mut graphics_queue_family_index: Option = None; + let mut compute_queue_family_index: Option = None; + let mut transfer_queue_family_index: Option = None; + + for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { + if config.with_graphics_queue { + let graphics_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::GRAPHICS); + + let presentation_valid = if config.with_window_surface { + let display_handle = world + .get_resource::() + .expect("DisplayHandleWrapper must be added before VulkanPlugin"); + + physical_device + .presentation_support(i as u32, &display_handle.0) + .expect("Failed to check presentation support") + } else { + true + }; + + if graphics_supported && presentation_valid { + graphics_queue_family_index = Some(i as u32); + } + } + + if config.with_compute_queue { + let compute_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::COMPUTE); + + if compute_supported { + compute_queue_family_index = Some(i as u32); + } + } + + if config.with_transfer_queue { + let transfer_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::TRANSFER); + + if transfer_supported { + transfer_queue_family_index = Some(i as u32); + } + } + } + + if !config.with_graphics_queue { + log::debug!("\t\t[SKIPPED] Graphics queue is not required"); + } else if graphics_queue_family_index.is_some() { + log::debug!("\t\t[OK] Graphics queue is supported"); + } else { + log::debug!("\t\t[FAILED] Graphics queue is not supported"); + return None; + } + + if !config.with_compute_queue { + log::debug!("\t\t[SKIPPED] Compute queue is not required"); + } else if compute_queue_family_index.is_some() { + log::debug!("\t\t[OK] Compute queue is supported"); + } else { + log::debug!("\t\t[FAILED] Compute queue is not supported"); + return None; + } + + if !config.with_transfer_queue { + log::debug!("\t\t[SKIPPED] Transfer queue is not required"); + } else if transfer_queue_family_index.is_some() { + log::debug!("\t\t[OK] Transfer queue is supported"); + } else { + log::debug!("\t\t[FAILED] Transfer queue is not supported"); + return None; + } + + Some(PickedQueuesInfo { + graphics_queue_family_index, + compute_queue_family_index, + transfer_queue_family_index, + }) +} + +fn check_physical_device( + world: &World, + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + log::debug!("Checking physical device"); + log::debug!("\tProperties"); + log::debug!("\t\tName: {}", physical_device.properties().device_name); + log::debug!("\t\tAPI version: {}", physical_device.api_version()); + log::debug!( + "\t\tDevice type: {:?}", + physical_device.properties().device_type + ); + log::debug!("\tRequired supports checking report"); + + let device_extensions = check_device_extensions_support(physical_device, config)?; + let picked_queues_info = check_queues_support(world, physical_device, config)?; + + let mut queue_create_infos = vec![]; + + if config.with_graphics_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), + ..Default::default() + }); + } + + if config.with_compute_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), + ..Default::default() + }); + } + + if config.with_transfer_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), + ..Default::default() + }); + } + + let (device, mut queues) = Device::new( + physical_device.clone(), + DeviceCreateInfo { + queue_create_infos, + enabled_extensions: device_extensions, + enabled_features: config.device_features, + ..Default::default() + }, + ) + .expect("Failed to create device"); + + let mut graphics_queue = None; + let mut compute_queue = None; + let mut transfer_queue = None; + + if config.with_graphics_queue { + graphics_queue = queues.next(); + } + + if config.with_compute_queue { + compute_queue = queues.next(); + } + + if config.with_transfer_queue { + transfer_queue = queues.next(); + } + + Some(PickedDevice { + device, + graphics_queue, + compute_queue, + transfer_queue, + }) +} diff --git a/crates/engine_vulkan/src/utils/instance.rs b/crates/engine_vulkan/src/utils/instance.rs new file mode 100644 index 0000000..dcda857 --- /dev/null +++ b/crates/engine_vulkan/src/utils/instance.rs @@ -0,0 +1,70 @@ +use std::sync::Arc; + +use bevy_ecs::world::World; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + VulkanLibrary, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + swapchain::Surface, +}; + +use crate::{VulkanConfig, VulkanInstance}; + +fn load_library() -> Arc { + let library = VulkanLibrary::new().unwrap(); + + log::debug!("Available Instance layers:"); + for layer in library.layer_properties().unwrap() { + log::debug!( + "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", + layer.name(), + layer.description(), + layer.implementation_version(), + layer.vulkan_version() + ); + } + + library +} + +pub fn create_and_insert_instance(world: &mut World, config: &VulkanConfig) { + let library = load_library(); + + let instance_extensions = { + if config.with_window_surface { + let display_handle = world + .get_resource::() + .expect("DisplayHandleWrapper must be added before VulkanPlugin"); + + Surface::required_extensions(&display_handle.0) + .expect("Failed to get surface required extensions") + } else { + InstanceExtensions::default() + } + }; + + let instance = Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: instance_extensions, + enabled_layers: config.instance_layers.clone(), + ..Default::default() + }, + ) + .expect("Failed to create vulkan instance"); + + log::debug!("Instance created"); + log::debug!( + "\t- Enabled extensions: {:?}", + instance.enabled_extensions() + ); + log::debug!("\t- Enabled layers: {:?}", instance.enabled_layers()); + log::debug!("\t- API version: {:?}", instance.api_version()); + log::debug!("\t- Max API version: {:?}", instance.max_api_version()); + log::debug!("\t- Flags: {:?}", instance.flags()); + + world.insert_resource(VulkanInstance(instance)); +} diff --git a/crates/engine_vulkan/src/utils/mod.rs b/crates/engine_vulkan/src/utils/mod.rs new file mode 100644 index 0000000..09e4b35 --- /dev/null +++ b/crates/engine_vulkan/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod device; +pub mod instance; diff --git a/crates/engine_vulkan/src/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs deleted file mode 100644 index 7bf3b3b..0000000 --- a/crates/engine_vulkan/src/vulkan_context.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::{any::Any, sync::Arc}; - -use bevy_app::App; -use bevy_ecs::resource::Resource; -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::{ - command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, - allocator::StandardCommandBufferAllocator, - }, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::{Device, Queue}, - instance::Instance, - memory::allocator::StandardMemoryAllocator, - swapchain::Surface, -}; -use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; - -use super::utils; - -#[derive(Resource)] -pub struct VulkanContext { - pub instance: Arc, - pub device: Arc, - pub graphics_queue: Arc, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, -} - -impl VulkanContext { - pub fn create_surface( - &self, - window: Arc, - ) -> Arc { - Surface::from_window(self.instance.clone(), window).unwrap() - } - - pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { - AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.graphics_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap() - } -} - -impl From<&App> for VulkanContext { - fn from(app: &App) -> Self { - let library = utils::load_library(); - - let world = app.world(); - - let display_handle: &DisplayHandleWrapper = - world.get_resource::().unwrap(); - - let enabled_extensions = Surface::required_extensions(&display_handle.0).unwrap(); - log::debug!("Surface required extensions: {enabled_extensions:?}"); - - let instance = utils::create_instance(library.clone(), enabled_extensions); - - let (device, mut queues) = utils::pick_graphics_device(&instance, &display_handle.0); - let graphics_queue = queues.next().unwrap(); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - - Self { - instance: instance.clone(), - device, - graphics_queue, - memory_allocator, - command_buffer_allocator, - descriptor_set_allocator, - } - } -} diff --git a/crates/engine_vulkan/src/window_render_context.rs b/crates/engine_vulkan/src/window_render_context.rs index 87ee958..95b3d90 100644 --- a/crates/engine_vulkan/src/window_render_context.rs +++ b/crates/engine_vulkan/src/window_render_context.rs @@ -5,12 +5,12 @@ use std::sync::Arc; use vulkano::image::view::ImageView; use vulkano::image::{Image, ImageUsage}; use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::swapchain::{Swapchain, SwapchainCreateInfo}; +use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use vulkano::sync::{self, GpuFuture}; use vulkano::{Validated, VulkanError}; use winit::window::Window; -use super::vulkan_context::VulkanContext; +use crate::{VulkanDevice, VulkanInstance}; #[derive(Resource)] pub struct WindowRenderContext { @@ -25,27 +25,36 @@ pub struct WindowRenderContext { impl From<&App> for WindowRenderContext { fn from(app: &App) -> Self { let world = app.world(); - let vulkan_context = world.get_resource::().unwrap(); - let window_handle = world.get_resource::().unwrap(); + let window_handle = world + .get_resource::() + .expect("Failed to find window handle"); + let vulkan_instance = world + .get_resource::() + .expect("Failed to find vulkan instance"); + let vulkan_device = world + .get_resource::() + .expect("Failed to find vulkan device"); + let window_size = window_handle.0.inner_size(); - let surface = vulkan_context.create_surface(window_handle.0.clone()); + let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone()) + .expect("Failed to create surface"); let (swapchain, images) = { - let surface_capabilities = vulkan_context - .device + let surface_capabilities = vulkan_device + .0 .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let (image_format, _) = vulkan_context - .device + let (image_format, _) = vulkan_device + .0 .physical_device() .surface_formats(&surface, Default::default()) .unwrap()[0]; Swapchain::new( - vulkan_context.device.clone(), + vulkan_device.0.clone(), surface, SwapchainCreateInfo { // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. @@ -74,7 +83,7 @@ impl From<&App> for WindowRenderContext { }; let recreate_swapchain = false; - let previous_frame_end = Some(sync::now(vulkan_context.device.clone()).boxed_send_sync()); + let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync()); Self { window: window_handle.0.clone(), diff --git a/flake.nix b/flake.nix index b00f3ab..9cef9c1 100644 --- a/flake.nix +++ b/flake.nix @@ -65,7 +65,7 @@ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; - RUST_LOG = "trace,rust_vulkan_test=trace"; + RUST_LOG = "debug,rust_vulkan_test=trace"; }; in { diff --git a/src/game/mod.rs b/src/game/mod.rs index 2078652..2c30bf8 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,6 +1,7 @@ use bevy_app::App; -use engine_vulkan::VulkanPlugin; +use engine_vulkan::{VulkanConfig, VulkanPlugin}; use engine_window::{WindowPlugin, config::WindowConfig}; +use vulkano::device::{DeviceExtensions, DeviceFeatures}; pub fn init(app: &mut App) { let window_config = WindowConfig { @@ -9,7 +10,27 @@ pub fn init(app: &mut App) { height: 600, }; - app.add_plugins((WindowPlugin { window_config }, VulkanPlugin)); + let device_extensions = DeviceExtensions { + khr_dynamic_rendering: true, + ..Default::default() + }; + + let device_features = DeviceFeatures { + dynamic_rendering: true, + ..Default::default() + }; + + let vulkan_config = VulkanConfig { + instance_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + device_extensions, + device_features, + ..Default::default() + }; + + app.add_plugins(( + WindowPlugin { window_config }, + VulkanPlugin { vulkan_config }, + )); // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); }