diff --git a/src/core/mod.rs b/src/core/mod.rs index bd7ce34..abaeeae 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,5 @@ pub mod app; pub mod camera; pub mod render; +pub mod vulkan; pub mod window; diff --git a/src/core/vulkan/context.rs b/src/core/vulkan/context.rs new file mode 100644 index 0000000..b4e9763 --- /dev/null +++ b/src/core/vulkan/context.rs @@ -0,0 +1,65 @@ +use std::sync::Arc; + +use bevy_ecs::system::Resource; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, + swapchain::Surface, +}; + +use crate::core::{app::App, window::raw_handle::DisplayHandleWrapper}; + +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 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(); + + 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/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs new file mode 100644 index 0000000..e80d56b --- /dev/null +++ b/src/core/vulkan/mod.rs @@ -0,0 +1,24 @@ +use context::VulkanContext; + +use super::app::App; + +mod context; +mod utils; + +#[derive(Debug, thiserror::Error)] +pub enum VulkanError { + #[error("Failed to create vulkan context")] + FailedToCreateVulkanContext, +} + +pub struct Vulkan; + +impl Vulkan { + pub fn new(app: &mut App) -> Result<(), VulkanError> { + let vulkan_context = VulkanContext::from(app as &App); + + app.world_mut().insert_resource(vulkan_context); + + Ok(()) + } +} diff --git a/src/core/vulkan/utils.rs b/src/core/vulkan/utils.rs new file mode 100644 index 0000000..046528f --- /dev/null +++ b/src/core/vulkan/utils.rs @@ -0,0 +1,137 @@ +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/src/core/window/mod.rs b/src/core/window/mod.rs index e600120..4368310 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,23 +1,44 @@ use config::WindowConfig; +use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; use state::WindowState; use winit::event_loop::EventLoop; use super::app::{App, AppExit}; pub mod config; +pub mod raw_handle; pub mod state; -pub fn init(app: &mut App, window_config: WindowConfig) { - let world = app.world_mut(); - world.insert_resource(window_config); - - let mut event_loop_builder = EventLoop::with_user_event(); - let event_loop = event_loop_builder.build().unwrap(); - - app.set_runner(Box::new(move |app| runner(app, event_loop))); +#[derive(Debug, thiserror::Error)] +pub enum WindowError { + #[error("Failed to create event loop")] + FailedToCreateEventLoop, } -fn runner(app: App, event_loop: EventLoop<()>) -> AppExit { +pub struct Window; + +impl Window { + pub fn new(app: &mut App, window_config: WindowConfig) -> Result<(), WindowError> { + let world = app.world_mut(); + world.insert_resource(window_config); + + let mut event_loop_builder = EventLoop::with_user_event(); + let event_loop = event_loop_builder + .build() + .map_err(|_| WindowError::FailedToCreateEventLoop)?; + + world.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle())); + + app.set_runner(Box::new(move |app| runner(app, event_loop))); + + Ok(()) + } +} + +fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { + app.world_mut() + .insert_resource(EventLoopProxyWrapper::new(event_loop.create_proxy())); + let mut window_state = WindowState::new(app); match event_loop.run_app(&mut window_state) { diff --git a/src/core/window/raw_handle.rs b/src/core/window/raw_handle.rs new file mode 100644 index 0000000..16b2178 --- /dev/null +++ b/src/core/window/raw_handle.rs @@ -0,0 +1,18 @@ +use bevy_ecs::system::Resource; +use winit::event_loop::EventLoopProxy; + +#[derive(Resource)] +pub struct EventLoopProxyWrapper(EventLoopProxy); + +impl EventLoopProxyWrapper { + pub fn new(event_loop: EventLoopProxy) -> Self { + Self(event_loop) + } + + pub fn proxy(&self) -> &EventLoopProxy { + &self.0 + } +} + +#[derive(Resource)] +pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); diff --git a/src/game/mod.rs b/src/game/mod.rs index ffee10f..0b7787d 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,6 +1,7 @@ use crate::core::{ app::App, - window::{self, config::WindowConfig}, + vulkan::Vulkan, + window::{Window, config::WindowConfig}, }; pub fn init(app: &mut App) { @@ -10,5 +11,6 @@ pub fn init(app: &mut App) { height: 600, }; - window::init(app, window_config); + Window::new(app, window_config).unwrap(); + Vulkan::new(app).unwrap(); } diff --git a/src/main.rs b/src/main.rs index c981713..8297797 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,8 @@ pub mod old_app; fn main() -> Result<(), impl Error> { env_logger::init(); - // run_new_app() - run_old_app() + run_new_app() + // run_old_app() } fn run_new_app() -> Result<(), impl Error> {