diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs index ccbc39d..8c869e9 100644 --- a/crates/engine_render/src/lib.rs +++ b/crates/engine_render/src/lib.rs @@ -11,6 +11,7 @@ use engine_vulkan::{ use engine_window::raw_handle::WindowWrapper; use window::WindowRenderPlugin; +pub mod render; pub mod window; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] @@ -65,6 +66,12 @@ impl Plugin for RenderPlugin { let mut render_app = SubApp::new(); render_app.update_schedule = Some(Render.intern()); render_app.add_schedule(Render::base_schedule()); + render_app.add_systems( + Render, + render::render_system + .in_set(RenderSystems::Render) + .run_if(render::can_render), + ); extract_app_resources(app.world_mut(), render_app.world_mut()); diff --git a/crates/engine_render/src/render.rs b/crates/engine_render/src/render.rs new file mode 100644 index 0000000..1a8c95e --- /dev/null +++ b/crates/engine_render/src/render.rs @@ -0,0 +1,122 @@ +use bevy_ecs::system::{Res, ResMut}; +use engine_vulkan::{VulkanCommandBufferAllocator, VulkanDevice, VulkanGraphicsQueue}; +use vulkano::{ + Validated, VulkanError, + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, + }, + render_pass::{AttachmentLoadOp, AttachmentStoreOp}, + swapchain::{SwapchainAcquireFuture, SwapchainPresentInfo, acquire_next_image}, + sync::{self, GpuFuture}, +}; + +use crate::window::{WindowSurface, WindowSurfaceData}; + +pub fn can_render(window_surface: Res) -> bool { + window_surface.surface.is_some() +} + +pub fn render_system( + mut window_surface: ResMut, + command_buffer_allocator: Res, + graphics_queue: Res, + device: Res, +) { + { + let surface = window_surface.surface.as_ref().unwrap(); + if surface.viewport.extent[0] == 0.0 || surface.viewport.extent[1] == 0.0 { + return; + } + } + + let (image_index, acquire_future) = + match acquire_image(&mut window_surface.surface.as_mut().unwrap()) { + Some(r) => r, + None => return, + }; + + let mut builder = AutoCommandBufferBuilder::primary( + command_buffer_allocator.0.clone(), + graphics_queue.0.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .expect("failed to create command buffer builder"); + + { + let surface = window_surface.surface.as_ref().unwrap(); + builder + .begin_rendering(RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 0.0].into()), + ..RenderingAttachmentInfo::image_view( + surface.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + .set_viewport(0, [surface.viewport.clone()].into_iter().collect()) + .unwrap(); + } + + builder.end_rendering().unwrap(); + + let command_buffer = builder.build().unwrap(); + + { + let surface = window_surface.surface.as_mut().unwrap(); + + let future = surface + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(graphics_queue.0.clone(), command_buffer) + .unwrap() + .then_swapchain_present( + graphics_queue.0.clone(), + SwapchainPresentInfo::swapchain_image_index(surface.swapchain.clone(), image_index), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + surface.previous_frame_end = Some(future.boxed_send_sync()); + } + Err(VulkanError::OutOfDate) => { + surface.recreate_swapchain = true; + surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync()); + } + Err(e) => { + println!("failed to flush future: {e}"); + surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync()); + } + } + } +} + +fn acquire_image(surface: &mut WindowSurfaceData) -> Option<(u32, SwapchainAcquireFuture)> { + surface + .previous_frame_end + .as_mut() + .unwrap() + .cleanup_finished(); + + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(surface.swapchain.clone(), None).map_err(Validated::unwrap) { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + surface.recreate_swapchain = true; + return None; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + surface.recreate_swapchain = true; + } + + Some((image_index, acquire_future)) +}