From dbe415d2623388faa1cf91d220e391e75b0c8994 Mon Sep 17 00:00:00 2001
From: Florian RICHER <florian.richer@protonmail.com>
Date: Sat, 24 May 2025 18:04:56 +0200
Subject: [PATCH] engine_render: Add first render

---
 crates/engine_render/src/lib.rs    |   7 ++
 crates/engine_render/src/render.rs | 122 +++++++++++++++++++++++++++++
 2 files changed, 129 insertions(+)
 create mode 100644 crates/engine_render/src/render.rs

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<WindowSurface>) -> bool {
+    window_surface.surface.is_some()
+}
+
+pub fn render_system(
+    mut window_surface: ResMut<WindowSurface>,
+    command_buffer_allocator: Res<VulkanCommandBufferAllocator>,
+    graphics_queue: Res<VulkanGraphicsQueue>,
+    device: Res<VulkanDevice>,
+) {
+    {
+        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))
+}