use std::sync::Arc; use bevy_app::{App, Plugin}; use bevy_ecs::{ resource::Resource, schedule::IntoScheduleConfigs, system::{Res, ResMut}, }; use engine_vulkan::{VulkanDevice, VulkanInstance}; use engine_window::raw_handle::WindowWrapper; use vulkano::{ image::{Image, ImageUsage, view::ImageView}, pipeline::graphics::viewport::Viewport, swapchain::{Surface, Swapchain, SwapchainCreateInfo}, sync::{self, GpuFuture}, }; use super::{Render, RenderApp, RenderSystems}; pub struct WindowSurfaceData { pub swapchain: Arc, pub attachment_image_views: Vec>, pub viewport: Viewport, pub recreate_swapchain: bool, pub previous_frame_end: Option>, } #[derive(Resource, Default)] pub struct WindowSurface { pub surface: Option, } pub struct WindowRenderPlugin; impl Plugin for WindowRenderPlugin { fn build(&self, app: &mut App) { let render_app = app .get_sub_app_mut(RenderApp) .expect("Failed to get RenderApp. Check is RenderPlugin is added."); render_app.init_resource::(); render_app.add_systems( Render, create_window_surface .in_set(RenderSystems::ManageViews) .run_if(need_create_window_surface) .before(need_update_window_surface), ); render_app.add_systems( Render, update_window_surface .in_set(RenderSystems::ManageViews) .run_if(need_update_window_surface), ); } } fn need_create_window_surface(window_surface: Res) -> bool { window_surface.surface.is_none() } fn create_window_surface( mut window_surface: ResMut, window_handle: Res, vulkan_instance: Res, vulkan_device: Res, ) { let window_size = window_handle.0.inner_size(); let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone()) .expect("Failed to create surface"); log::debug!("Surface created"); let (swapchain, images) = { let surface_capabilities = vulkan_device .0 .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); let (image_format, _) = vulkan_device .0 .physical_device() .surface_formats(&surface, Default::default()) .unwrap()[0]; Swapchain::new( vulkan_device.0.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() }; log_swapchain_info(&swapchain, false); 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, }; log_viewport_info(&viewport, false); let recreate_swapchain = false; let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync()); window_surface.surface = Some(WindowSurfaceData { swapchain, attachment_image_views, viewport, recreate_swapchain, previous_frame_end, }); } fn window_size_dependent_setup(images: &[Arc]) -> Vec> { images .iter() .map(|image| ImageView::new_default(image.clone()).unwrap()) .collect::>() } fn need_update_window_surface(window_surface: Res) -> bool { match &window_surface.surface { Some(surface) => surface.recreate_swapchain, None => false, } } fn update_window_surface( mut window_surface: ResMut, window_handle: Res, ) { if window_surface.surface.is_none() { return; } let window_surface = window_surface.surface.as_mut().unwrap(); if !window_surface.recreate_swapchain { return; } let window_size = window_handle.0.inner_size(); let (new_swapchain, new_images) = window_surface .swapchain .recreate(SwapchainCreateInfo { image_extent: window_size.into(), ..window_surface.swapchain.create_info() }) .expect("Failed to recreate swapchain"); window_surface.swapchain = new_swapchain; window_surface.attachment_image_views = window_size_dependent_setup(&new_images); window_surface.viewport.extent = window_size.into(); window_surface.recreate_swapchain = false; log_swapchain_info(&window_surface.swapchain, true); log_viewport_info(&window_surface.viewport, true); } fn log_swapchain_info(swapchain: &Swapchain, recreate_swapchain: bool) { if recreate_swapchain { log::debug!("Swapchain recreated"); } else { log::debug!("Swapchain created"); } log::debug!( "\tMin image count: {}", swapchain.create_info().min_image_count ); log::debug!("\tImage format: {:?}", swapchain.create_info().image_format); log::debug!("\tImage extent: {:?}", swapchain.create_info().image_extent); log::debug!("\tImage usage: {:?}", swapchain.create_info().image_usage); log::debug!( "\tComposite alpha: {:?}", swapchain.create_info().composite_alpha ); log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode); log::debug!( "\tImage sharing: {:?}", swapchain.create_info().image_sharing ); log::debug!( "\tPre transform: {:?}", swapchain.create_info().pre_transform ); log::debug!( "\tComposite alpha: {:?}", swapchain.create_info().composite_alpha ); log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode); log::debug!( "\tFull screen exclusive: {:?}", swapchain.create_info().full_screen_exclusive ); } fn log_viewport_info(viewport: &Viewport, recreate_viewport: bool) { if recreate_viewport { log::debug!("Viewport recreated"); } else { log::debug!("Viewport created"); } log::debug!("\tOffset: {:?}", viewport.offset); log::debug!("\tExtent: {:?}", viewport.extent); log::debug!("\tDepth range: {:?}", viewport.depth_range); }