diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs index e80d56b..fe22fb3 100644 --- a/src/core/vulkan/mod.rs +++ b/src/core/vulkan/mod.rs @@ -1,9 +1,11 @@ -use context::VulkanContext; +use vulkan_context::VulkanContext; +use window_render_context::WindowRenderContext; use super::app::App; -mod context; mod utils; +mod vulkan_context; +mod window_render_context; #[derive(Debug, thiserror::Error)] pub enum VulkanError { @@ -16,9 +18,11 @@ 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); + let window_render_context = WindowRenderContext::from(app as &App); + app.world_mut().insert_resource(window_render_context); + Ok(()) } } diff --git a/src/core/vulkan/context.rs b/src/core/vulkan/vulkan_context.rs similarity index 69% rename from src/core/vulkan/context.rs rename to src/core/vulkan/vulkan_context.rs index b4e9763..f59d9a3 100644 --- a/src/core/vulkan/context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -1,14 +1,18 @@ -use std::sync::Arc; +use std::{any::Any, sync::Arc}; use bevy_ecs::system::Resource; use vulkano::{ - command_buffer::allocator::StandardCommandBufferAllocator, + 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 crate::core::{app::App, window::raw_handle::DisplayHandleWrapper}; @@ -25,6 +29,24 @@ pub struct VulkanContext { 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(); diff --git a/src/core/vulkan/window_render_context.rs b/src/core/vulkan/window_render_context.rs new file mode 100644 index 0000000..1c653a7 --- /dev/null +++ b/src/core/vulkan/window_render_context.rs @@ -0,0 +1,117 @@ +use bevy_ecs::system::Resource; +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::sync::{self, GpuFuture}; +use vulkano::{Validated, VulkanError}; +use winit::window::Window; + +use crate::core::app::App; +use crate::core::window::raw_handle::WindowWrapper; + +use super::vulkan_context::VulkanContext; + +#[derive(Resource)] +pub struct WindowRenderContext { + pub window: Arc, + pub swapchain: Arc, + pub attachment_image_views: Vec>, + pub viewport: Viewport, + pub recreate_swapchain: bool, + pub previous_frame_end: Option>, +} + +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_size = window_handle.0.inner_size(); + + let surface = vulkan_context.create_surface(window_handle.0.clone()); + + let (swapchain, images) = { + let surface_capabilities = vulkan_context + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + let (image_format, _) = vulkan_context + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + vulkan_context.device.clone(), + surface, + SwapchainCreateInfo { + // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; + + let attachment_image_views = window_size_dependent_setup(&images); + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let recreate_swapchain = false; + let previous_frame_end = Some(sync::now(vulkan_context.device.clone()).boxed_send_sync()); + + Self { + window: window_handle.0.clone(), + swapchain, + attachment_image_views, + viewport, + recreate_swapchain, + previous_frame_end, + } + } +} + +impl WindowRenderContext { + pub fn update_swapchain(&mut self) -> Result<(), Validated> { + if !self.recreate_swapchain { + return Ok(()); + } + + let window_size = self.window.inner_size(); + let (new_swapchain, new_images) = self.swapchain.recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..self.swapchain.create_info() + })?; + + self.swapchain = new_swapchain; + self.attachment_image_views = window_size_dependent_setup(&new_images); + self.viewport.extent = window_size.into(); + self.recreate_swapchain = false; + + Ok(()) + } +} + +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +} diff --git a/src/core/window/raw_handle.rs b/src/core/window/raw_handle.rs index 16b2178..ad9c8dd 100644 --- a/src/core/window/raw_handle.rs +++ b/src/core/window/raw_handle.rs @@ -1,5 +1,7 @@ +use std::sync::Arc; + use bevy_ecs::system::Resource; -use winit::event_loop::EventLoopProxy; +use winit::{event_loop::EventLoopProxy, window::Window}; #[derive(Resource)] pub struct EventLoopProxyWrapper(EventLoopProxy); @@ -16,3 +18,6 @@ impl EventLoopProxyWrapper { #[derive(Resource)] pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); + +#[derive(Resource)] +pub struct WindowWrapper(pub Arc); diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 94bfcc7..0b0966e 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,23 +1,22 @@ +use std::sync::Arc; + use bevy_ecs::world::World; use winit::{ - application::ApplicationHandler, - event::WindowEvent, - event_loop::ActiveEventLoop, - window::{Window, WindowId}, + application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, + window::WindowId, }; use crate::core::app::App; -use super::config::WindowConfig; +use super::{config::WindowConfig, raw_handle::WindowWrapper}; pub struct WindowState { app: App, - window: Option, } impl WindowState { pub fn new(app: App) -> Self { - Self { app, window: None } + Self { app } } fn world(&self) -> &World { @@ -30,7 +29,9 @@ impl ApplicationHandler for WindowState { let window_config = self.world().get_resource::().unwrap(); let window = event_loop.create_window(window_config.into()).unwrap(); - self.window = Some(window); + self.app + .world_mut() + .insert_resource(WindowWrapper(Arc::new(window))); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {