From 42db1a33a0e662b4ffbf2ccff60ac53d224c3a39 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 1 Apr 2025 22:00:04 +0200 Subject: [PATCH 01/28] Try refactor with cursor --- .cursor/mcp.json | 10 + src/main.rs | 17 +- src/renderer/app.rs | 383 +++++++++---------- src/renderer/components.rs | 71 ++++ src/renderer/mod.rs | 11 +- src/renderer/pipelines/mod.rs | 1 - src/renderer/pipelines/triangle_pipeline.rs | 111 ------ src/renderer/render_context.rs | 102 ----- src/renderer/scene.rs | 151 -------- src/vulkan/context.rs | 135 +++++++ src/vulkan/mod.rs | 4 + src/vulkan/pipeline/mod.rs | 30 ++ src/vulkan/pipeline/triangle.rs | 127 ++++++ src/vulkan/renderer.rs | 167 ++++++++ src/vulkan/resources/buffer.rs | 65 ++++ src/vulkan/resources/descriptor.rs | 47 +++ src/vulkan/resources/mod.rs | 3 + src/{renderer => vulkan/resources}/vertex.rs | 14 +- 18 files changed, 855 insertions(+), 594 deletions(-) create mode 100644 .cursor/mcp.json create mode 100644 src/renderer/components.rs delete mode 100644 src/renderer/pipelines/mod.rs delete mode 100644 src/renderer/pipelines/triangle_pipeline.rs delete mode 100644 src/renderer/render_context.rs delete mode 100644 src/renderer/scene.rs create mode 100644 src/vulkan/context.rs create mode 100644 src/vulkan/mod.rs create mode 100644 src/vulkan/pipeline/mod.rs create mode 100644 src/vulkan/pipeline/triangle.rs create mode 100644 src/vulkan/renderer.rs create mode 100644 src/vulkan/resources/buffer.rs create mode 100644 src/vulkan/resources/descriptor.rs create mode 100644 src/vulkan/resources/mod.rs rename src/{renderer => vulkan/resources}/vertex.rs (82%) diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 0000000..86d37b4 --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,10 @@ +{ + "mcpServers": { + "run-program": { + "command": "cargo", + "args": [ + "run" + ] + } + } +} diff --git a/src/main.rs b/src/main.rs index 6c01c40..ac0d7c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,14 @@ -use std::error::Error; -use winit::event_loop::{ControlFlow, EventLoop}; +use winit::event_loop::EventLoop; mod renderer; +mod vulkan; -fn main() -> Result<(), impl Error> { - env_logger::init(); +use renderer::app::App; +use vulkan::context::VulkanContext; +fn main() { let event_loop = EventLoop::new().unwrap(); - event_loop.set_control_flow(ControlFlow::Poll); - - let mut app = renderer::App::new(&event_loop); - - event_loop.run_app(&mut app) + let vulkan_context = VulkanContext::new(&event_loop).unwrap(); + let mut app = App::new(vulkan_context); + event_loop.run_app(&mut app).unwrap(); } diff --git a/src/renderer/app.rs b/src/renderer/app.rs index f791616..923d70c 100644 --- a/src/renderer/app.rs +++ b/src/renderer/app.rs @@ -1,160 +1,167 @@ -use crate::renderer::render_context::RenderContext; -use crate::renderer::Scene; +use crate::renderer::components::{Entity, Material, Mesh, Transform}; +use crate::vulkan::context::VulkanContext; +use crate::vulkan::pipeline::{Pipeline, triangle::TrianglePipeline}; +use crate::vulkan::renderer::VulkanRenderer; +use crate::vulkan::resources::vertex::{MVPData, Vertex2D}; +use std::error::Error; use std::sync::Arc; -use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; -use vulkano::buffer::BufferUsage; -use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; +use vulkano::VulkanError; +use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage}; use vulkano::command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + RenderingAttachmentInfo, RenderingInfo, }; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; -use vulkano::device::physical::PhysicalDeviceType; -use vulkano::device::{ - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, -}; -use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; -use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; +use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; +use vulkano::pipeline::{Pipeline as VulkanPipeline, PipelineBindPoint}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{acquire_next_image, Surface, SwapchainPresentInfo}; -use vulkano::sync::GpuFuture; -use vulkano::{sync, Validated, Version, VulkanError, VulkanLibrary}; +use vulkano::swapchain::Surface; use winit::application::ApplicationHandler; use winit::event::WindowEvent; -use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; pub struct App { - pub instance: Arc, - pub device: Arc, - pub queue: Arc, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub uniform_buffer_allocator: SubbufferAllocator, - pub descriptor_set_allocator: Arc, - - pub rcx: Option, - scene: Option, + pub vulkan_context: VulkanContext, + pub renderer: Option, + pub entities: Vec, } impl App { - pub fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - - for layer in library.layer_properties().unwrap() { - log::debug!("Available layer: {}", layer.name()); + pub fn new(vulkan_context: VulkanContext) -> Self { + Self { + vulkan_context, + renderer: None, + entities: Vec::new(), } + } - let required_extensions = Surface::required_extensions(event_loop).unwrap(); + pub fn setup_test_entities(&mut self) -> Result<(), Box> { + // Créer un pipeline de test + let pipeline = TrianglePipeline::new(&self.vulkan_context.device); - let instance = 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() + // Créer un buffer de vertex pour un triangle + let vertices = [ + Vertex2D { + position: [-0.5, -0.5], + color: [1.0, 0.0, 0.0], // Rouge }, - ) - .unwrap(); + Vertex2D { + position: [0.5, -0.5], + color: [0.0, 1.0, 0.0], // Vert + }, + Vertex2D { + position: [0.0, 0.5], + color: [0.0, 0.0, 1.0], // Bleu + }, + ]; - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() + let vertex_buffer = + Vertex2D::create_buffer(vertices.to_vec(), &self.vulkan_context.memory_allocator) + .unwrap(); + + // Créer un buffer uniform pour les matrices MVP + let mvp_data = MVPData { + world: [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], + view: [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], + projection: [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ], }; - let (physical_device, queue_family_index) = 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| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .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, - }) - .expect("no suitable physical device found"); - - 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); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, - ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, + let uniform_buffer = Buffer::from_data( + self.vulkan_context.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::UNIFORM_BUFFER, ..Default::default() }, - ) - .unwrap(); - - let 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 uniform_buffer_allocator = SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, + AllocationCreateInfo { memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - ); + mvp_data, + ) + .unwrap(); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); + // Créer un descriptor set de test + let descriptor_set = DescriptorSet::new( + self.vulkan_context.descriptor_set_allocator.clone(), + pipeline + .get_pipeline() + .layout() + .set_layouts() + .get(0) + .unwrap() + .clone(), + [WriteDescriptorSet::buffer(0, uniform_buffer)], + [], + )?; - Self { - instance, - device, - queue, - memory_allocator, - command_buffer_allocator, - uniform_buffer_allocator, - descriptor_set_allocator, - rcx: None, - scene: None, + let material = Material { + pipeline: pipeline.get_pipeline().clone(), + descriptor_set, + }; + + // Créer quelques entités de test + let mut entities = Vec::new(); + for i in 0..3 { + entities.push(Entity { + mesh: Mesh { + vertex_buffer: vertex_buffer.clone(), + vertex_count: 3, + instance_count: 1, + }, + material: material.clone(), + transform: Transform { + position: [i as f32 * 0.5 - 0.5, 0.0, 0.0], + rotation: [0.0, 0.0, 0.0], + scale: [1.0, 1.0, 1.0], + }, + }); } + self.entities = entities; + + Ok(()) + } + + pub fn render( + &self, + command_buffer: &mut AutoCommandBufferBuilder, + ) -> Result<(), Box> { + for entity in &self.entities { + command_buffer + .bind_pipeline_graphics(entity.material.pipeline.clone()) + .unwrap() + .bind_descriptor_sets( + PipelineBindPoint::Graphics, + entity.material.pipeline.layout().clone(), + 0, + entity.material.descriptor_set.clone(), + ) + .unwrap() + .bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone()) + .unwrap(); + unsafe { + command_buffer + .draw(entity.mesh.vertex_count, 1, 0, 0) + .unwrap(); + } + } + Ok(()) } } @@ -169,10 +176,17 @@ impl ApplicationHandler for App { let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); + let surface = + Surface::from_window(self.vulkan_context.instance.clone(), window.clone()).unwrap(); - self.rcx = Some(RenderContext::new(window, surface, &self.device)); - self.scene = Some(Scene::load(&self).unwrap()); + self.renderer = Some(VulkanRenderer::new( + window, + surface, + self.vulkan_context.device.clone(), + self.vulkan_context.queue.clone(), + )); + + self.setup_test_entities().unwrap(); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { @@ -182,49 +196,25 @@ impl ApplicationHandler for App { event_loop.exit(); } WindowEvent::Resized(_) => { - let rcx = self.rcx.as_mut().unwrap(); - rcx.recreate_swapchain = true; + let renderer = self.renderer.as_mut().unwrap(); + renderer.recreate_swapchain = true; } WindowEvent::RedrawRequested => { - let (image_index, acquire_future) = { - let rcx = self.rcx.as_mut().unwrap(); - let window_size = rcx.window.inner_size(); - - if window_size.width == 0 || window_size.height == 0 { - return; - } - - rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - rcx.update_swapchain().unwrap(); - - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - if suboptimal { - rcx.recreate_swapchain = true; - } - - (image_index, acquire_future) - }; + let (image_index, acquire_future) = + match self.renderer.as_mut().unwrap().begin_frame() { + Ok(r) => r, + Err(VulkanError::OutOfDate) => return, + Err(e) => panic!("failed to acquire next image: {e}"), + }; let mut builder = AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.queue.queue_family_index(), + self.vulkan_context.command_buffer_allocator.clone(), + self.vulkan_context.queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, ) .unwrap(); { - let rcx = self.rcx.as_ref().unwrap(); builder .begin_rendering(RenderingInfo { color_attachments: vec![Some(RenderingAttachmentInfo { @@ -232,64 +222,41 @@ impl ApplicationHandler for App { store_op: AttachmentStoreOp::Store, clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), ..RenderingAttachmentInfo::image_view( - rcx.attachment_image_views[image_index as usize].clone(), + self.renderer.as_ref().unwrap().attachment_image_views + [image_index as usize] + .clone(), ) })], ..Default::default() }) .unwrap() - .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) + .set_viewport( + 0, + [self.renderer.as_ref().unwrap().viewport.clone()] + .into_iter() + .collect(), + ) .unwrap(); } - if let Some(scene) = self.scene.as_ref() { - scene.render(&self, &mut builder).unwrap(); - } - - builder.end_rendering().unwrap(); - - let command_buffer = builder.build().unwrap(); + self.render(&mut builder).unwrap(); { - let rcx = self.rcx.as_mut().unwrap(); - - let future = rcx - .previous_frame_end - .take() - .unwrap() - .join(acquire_future) - .then_execute(self.queue.clone(), command_buffer) - .unwrap() - .then_swapchain_present( - self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index( - rcx.swapchain.clone(), - image_index, - ), - ) - .then_signal_fence_and_flush(); - - match future.map_err(Validated::unwrap) { - Ok(future) => { - rcx.previous_frame_end = Some(future.boxed()); - } - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - } - Err(e) => { - println!("failed to flush future: {e}"); - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - } - } + builder.end_rendering().unwrap(); } + + self.renderer + .as_mut() + .unwrap() + .end_frame(image_index, acquire_future, builder) + .unwrap(); } _ => {} } } fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let rcx = self.rcx.as_mut().unwrap(); - rcx.window.request_redraw(); + let renderer = self.renderer.as_mut().unwrap(); + renderer.window.request_redraw(); } } diff --git a/src/renderer/components.rs b/src/renderer/components.rs new file mode 100644 index 0000000..968539e --- /dev/null +++ b/src/renderer/components.rs @@ -0,0 +1,71 @@ +use std::sync::Arc; +use vulkano::buffer::Subbuffer; +use vulkano::buffer::allocator::SubbufferAllocator; +use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; +use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; +use vulkano::descriptor_set::DescriptorSet; +use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; +use vulkano::device::Device; +use vulkano::memory::allocator::StandardMemoryAllocator; +use vulkano::pipeline::GraphicsPipeline; +use vulkano::pipeline::Pipeline; + +use crate::vulkan::resources::vertex::Vertex2D; + +#[derive(Clone)] +pub struct Mesh { + pub vertex_buffer: Subbuffer<[Vertex2D]>, + pub vertex_count: u32, + pub instance_count: u32, +} + +#[derive(Clone)] +pub struct Material { + pub pipeline: Arc, + pub descriptor_set: Arc, +} + +#[derive(Clone)] +pub struct Transform { + pub position: [f32; 3], + pub rotation: [f32; 3], + pub scale: [f32; 3], +} + +#[derive(Clone)] +pub struct RenderResources { + pub device: Arc, + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub uniform_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +pub struct Entity { + pub mesh: Mesh, + pub material: Material, + pub transform: Transform, +} + +pub fn render_system( + _resources: &RenderResources, + builder: &mut AutoCommandBufferBuilder, + entities: &[Entity], +) -> Result<(), Box> { + for entity in entities { + unsafe { + builder + .bind_pipeline_graphics(entity.material.pipeline.clone())? + .bind_descriptor_sets( + vulkano::pipeline::PipelineBindPoint::Graphics, + entity.material.pipeline.layout().clone(), + 0, + entity.material.descriptor_set.clone(), + )? + .bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone())? + .draw(entity.mesh.vertex_count, entity.mesh.instance_count, 0, 0)?; + } + } + + Ok(()) +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index bbcff9a..13545c4 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,9 +1,2 @@ -mod app; -mod pipelines; -mod render_context; -mod vertex; -pub use app::App; - -mod scene; -pub use scene::Scene; -pub use vertex::Vertex2D; +pub mod app; +pub mod components; diff --git a/src/renderer/pipelines/mod.rs b/src/renderer/pipelines/mod.rs deleted file mode 100644 index e5f30a7..0000000 --- a/src/renderer/pipelines/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod triangle_pipeline; diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/renderer/pipelines/triangle_pipeline.rs deleted file mode 100644 index f6001f7..0000000 --- a/src/renderer/pipelines/triangle_pipeline.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::collections::BTreeMap; -use std::error::Error; -use std::sync::Arc; -use vulkano::descriptor_set::layout::{ - DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, -}; -use vulkano::device::Device; -use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; -use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; -use vulkano::pipeline::graphics::multisample::MultisampleState; -use vulkano::pipeline::graphics::rasterization::RasterizationState; -use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; -use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; -use vulkano::pipeline::graphics::viewport::ViewportState; -use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; -use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; -use vulkano::pipeline::{ - DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, -}; -use vulkano::shader::{EntryPoint, ShaderStages}; -use vulkano::swapchain::Swapchain; - -use crate::renderer::Vertex2D; - -pub mod shaders { - pub mod vs { - vulkano_shaders::shader! { - ty: "vertex", - path: r"res/shaders/vertex.vert", - } - } - - pub mod fs { - vulkano_shaders::shader! { - ty: "fragment", - path: r"res/shaders/vertex.frag", - } - } -} - -pub fn create_triangle_pipeline( - device: &Arc, - swapchain: &Arc, -) -> Result, Box> { - let (vs, fs) = load_shaders(device)?; - let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; - - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - - let mut bindings = BTreeMap::::new(); - let mut descriptor_set_layout_binding = - DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer); - descriptor_set_layout_binding.stages = ShaderStages::VERTEX; - bindings.insert(0, descriptor_set_layout_binding); - - let descriptor_set_layout = DescriptorSetLayoutCreateInfo { - bindings, - ..Default::default() - }; - - let create_info = PipelineDescriptorSetLayoutCreateInfo { - set_layouts: vec![descriptor_set_layout], - flags: PipelineLayoutCreateFlags::default(), - push_constant_ranges: vec![], - } - .into_pipeline_layout_create_info(device.clone())?; - - let layout = PipelineLayout::new(device.clone(), create_info)?; - - let subpass = PipelineRenderingCreateInfo { - color_attachment_formats: vec![Some(swapchain.image_format())], - ..Default::default() - }; - - let pipeline = GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.color_attachment_formats.len() as u32, - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - )?; - - Ok(pipeline) -} - -fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> { - let vs = shaders::vs::load(device.clone())? - .entry_point("main") - .ok_or("Failed find main entry point of vertex shader".to_string())?; - - let fs = shaders::fs::load(device.clone())? - .entry_point("main") - .ok_or("Failed find main entry point of fragment shader".to_string())?; - - Ok((vs, fs)) -} diff --git a/src/renderer/render_context.rs b/src/renderer/render_context.rs deleted file mode 100644 index dd7e840..0000000 --- a/src/renderer/render_context.rs +++ /dev/null @@ -1,102 +0,0 @@ -use std::sync::Arc; -use vulkano::device::Device; -use vulkano::image::view::ImageView; -use vulkano::image::{Image, ImageUsage}; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; -use vulkano::sync::GpuFuture; -use vulkano::{sync, Validated, VulkanError}; -use winit::window::Window; - -pub struct RenderContext { - pub(super) window: Arc, - pub(super) swapchain: Arc, - pub(super) attachment_image_views: Vec>, - pub(super) viewport: Viewport, - pub(super) recreate_swapchain: bool, - pub(super) previous_frame_end: Option>, -} - -impl RenderContext { - pub fn new(window: Arc, surface: Arc, device: &Arc) -> Self { - let window_size = window.inner_size(); - - let (swapchain, images) = { - let surface_capabilities = device - .physical_device() - .surface_capabilities(&surface, Default::default()) - .unwrap(); - - let (image_format, _) = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap()[0]; - - Swapchain::new( - 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(device.clone()).boxed()); - - Self { - window, - swapchain, - attachment_image_views, - viewport, - recreate_swapchain, - previous_frame_end, - } - } - - 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/renderer/scene.rs b/src/renderer/scene.rs deleted file mode 100644 index e3c6b58..0000000 --- a/src/renderer/scene.rs +++ /dev/null @@ -1,151 +0,0 @@ -use crate::renderer::pipelines::triangle_pipeline::shaders::vs; -use glam::{Mat3, Mat4, Vec3}; -use std::error::Error; -use std::sync::Arc; -use std::time::Instant; -use vulkano::buffer::Subbuffer; -use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; -use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; -use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; - -use crate::renderer::{pipelines::triangle_pipeline::create_triangle_pipeline, App, Vertex2D}; - -const VERTICES: [Vertex2D; 12] = [ - // Triangle en haut à gauche - Vertex2D { - position: [-0.5, -0.75], - color: [1.0, 0.0, 0.0], - }, - Vertex2D { - position: [-0.75, -0.25], - color: [0.0, 1.0, 0.0], - }, - Vertex2D { - position: [-0.25, -0.25], - color: [0.0, 0.0, 1.0], - }, - // Triangle en bas à gauche - Vertex2D { - position: [-0.5, 0.25], - color: [0.5, 0.5, 0.5], - }, - Vertex2D { - position: [-0.75, 0.75], - color: [0.2, 0.8, 0.2], - }, - Vertex2D { - position: [-0.25, 0.75], - color: [0.8, 0.2, 0.2], - }, - // Triangle en haut à droite - Vertex2D { - position: [0.5, -0.75], - color: [1.0, 1.0, 0.0], - }, - Vertex2D { - position: [0.25, -0.25], - color: [0.0, 1.0, 1.0], - }, - Vertex2D { - position: [0.75, -0.25], - color: [1.0, 0.0, 1.0], - }, - // Triangle en bas à droite - Vertex2D { - position: [0.5, 0.25], - color: [0.1, 0.5, 0.8], - }, - Vertex2D { - position: [0.25, 0.75], - color: [0.8, 0.6, 0.1], - }, - Vertex2D { - position: [0.75, 0.75], - color: [0.3, 0.4, 0.6], - }, -]; - -pub struct Scene { - pipeline: Arc, - vertex_buffer: Subbuffer<[Vertex2D]>, - - rotation_start: Instant, -} - -impl Scene { - pub fn load(app: &App) -> Result> { - let pipeline = create_triangle_pipeline(&app.device, &app.rcx.as_ref().unwrap().swapchain)?; - let vertex_buffer = - Vertex2D::create_buffer(Vec::from_iter(VERTICES), &app.memory_allocator)?; - - Ok(Scene { - pipeline, - vertex_buffer, - rotation_start: Instant::now(), - }) - } - - pub fn render( - &self, - app: &App, - builder: &mut AutoCommandBufferBuilder, - ) -> Result<(), Box> { - let vertex_count = self.vertex_buffer.len() as u32; - let instance_count = vertex_count / 3; - - let uniform_buffer = self.get_uniform_buffer(app); - let layout = &self.pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new( - app.descriptor_set_allocator.clone(), - layout.clone(), - [WriteDescriptorSet::buffer(0, uniform_buffer)], - [], - ) - .unwrap(); - - unsafe { - builder - .bind_pipeline_graphics(self.pipeline.clone())? - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - self.pipeline.layout().clone(), - 0, - descriptor_set, - )? - .bind_vertex_buffers(0, self.vertex_buffer.clone())? - .draw(vertex_count, instance_count, 0, 0)?; - } - - Ok(()) - } - - fn get_uniform_buffer(&self, app: &App) -> Subbuffer { - let swapchain = &app.rcx.as_ref().unwrap().swapchain; - let elapsed = self.rotation_start.elapsed(); - let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; - let rotation = Mat3::from_rotation_y(rotation as f32); - - // NOTE: This teapot was meant for OpenGL where the origin is at the lower left - // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. - let aspect_ratio = swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; - - let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0); - let view = Mat4::look_at_rh( - Vec3::new(0.3, 0.3, 1.0), - Vec3::new(0.0, 0.0, 0.0), - Vec3::new(0.0, -1.0, 0.0), - ); - let scale = Mat4::from_scale(Vec3::splat(1.0)); - - let uniform_data = vs::MVPData { - world: Mat4::from_mat3(rotation).to_cols_array_2d(), - view: (view * scale).to_cols_array_2d(), - projection: proj.to_cols_array_2d(), - }; - - let buffer = app.uniform_buffer_allocator.allocate_sized().unwrap(); - *buffer.write().unwrap() = uniform_data; - - buffer - } -} diff --git a/src/vulkan/context.rs b/src/vulkan/context.rs new file mode 100644 index 0000000..bd1aeba --- /dev/null +++ b/src/vulkan/context.rs @@ -0,0 +1,135 @@ +use std::sync::Arc; +use vulkano::buffer::BufferUsage; +use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; +use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; +use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; +use vulkano::device::physical::PhysicalDeviceType; +use vulkano::device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, +}; +use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; +use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; +use vulkano::swapchain::Surface; +use vulkano::{Version, VulkanLibrary}; +use winit::event_loop::EventLoop; + +pub struct VulkanContext { + pub instance: Arc, + pub device: Arc, + pub queue: Arc, + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub uniform_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +impl VulkanContext { + pub fn new(event_loop: &EventLoop<()>) -> Result> { + let library = VulkanLibrary::new().unwrap(); + for layer in library.layer_properties().unwrap() { + log::debug!("Available layer: {}", layer.name()); + } + + let required_extensions = Surface::required_extensions(event_loop).unwrap(); + let instance = Instance::new( + library, + InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + ..Default::default() + }, + ) + .unwrap(); + + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + let (physical_device, queue_family_index) = 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| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(_i, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS)) + .map(|i| (p, i as u32)) + }) + .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, + }) + .expect("no suitable physical device found"); + + 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); + + let (device, mut queues) = Device::new( + physical_device, + DeviceCreateInfo { + queue_create_infos: vec![QueueCreateInfo { + queue_family_index, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + ..Default::default() + }, + ) + .unwrap(); + + let 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 uniform_buffer_allocator = Arc::new(SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::UNIFORM_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + )); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + + Ok(Self { + instance, + device, + queue, + memory_allocator, + command_buffer_allocator, + uniform_buffer_allocator, + descriptor_set_allocator, + }) + } +} diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs new file mode 100644 index 0000000..8a4bdd9 --- /dev/null +++ b/src/vulkan/mod.rs @@ -0,0 +1,4 @@ +pub mod context; +pub mod pipeline; +pub mod renderer; +pub mod resources; diff --git a/src/vulkan/pipeline/mod.rs b/src/vulkan/pipeline/mod.rs new file mode 100644 index 0000000..f9ba6d5 --- /dev/null +++ b/src/vulkan/pipeline/mod.rs @@ -0,0 +1,30 @@ +pub mod triangle; + +use std::sync::Arc; +use vulkano::device::Device; +use vulkano::pipeline::GraphicsPipeline; + +pub trait Pipeline { + fn create_pipeline(device: &Arc) -> Arc; + fn get_pipeline(&self) -> &Arc; +} + +pub struct PipelineManager { + pipelines: std::collections::HashMap>, +} + +impl PipelineManager { + pub fn new() -> Self { + Self { + pipelines: std::collections::HashMap::new(), + } + } + + pub fn register_pipeline(&mut self, name: String, pipeline: Arc) { + self.pipelines.insert(name, pipeline); + } + + pub fn get_pipeline(&self, name: &str) -> Option<&Arc> { + self.pipelines.get(name) + } +} diff --git a/src/vulkan/pipeline/triangle.rs b/src/vulkan/pipeline/triangle.rs new file mode 100644 index 0000000..c625e66 --- /dev/null +++ b/src/vulkan/pipeline/triangle.rs @@ -0,0 +1,127 @@ +use std::collections::BTreeMap; +use std::error::Error; +use std::sync::Arc; +use vulkano::descriptor_set::layout::{ + DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, +}; +use vulkano::device::Device; +use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; +use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; +use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; +use vulkano::pipeline::graphics::multisample::MultisampleState; +use vulkano::pipeline::graphics::rasterization::RasterizationState; +use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; +use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; +use vulkano::pipeline::graphics::viewport::ViewportState; +use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; +use vulkano::pipeline::{ + DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, +}; +use vulkano::shader::{EntryPoint, ShaderStages}; + +use crate::vulkan::resources::vertex::Vertex2D; + +use super::Pipeline; + +mod shaders { + pub mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/vertex.vert", + } + } + + pub mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/vertex.frag", + } + } +} + +pub struct TrianglePipeline { + pipeline: Arc, +} + +impl super::Pipeline for TrianglePipeline { + fn create_pipeline(device: &Arc) -> Arc { + let (vs, fs) = load_shaders(device).unwrap(); + let vertex_input_state = Vertex2D::per_vertex().definition(&vs).unwrap(); + + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + let mut bindings = BTreeMap::::new(); + let mut descriptor_set_layout_binding = + DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer); + descriptor_set_layout_binding.stages = ShaderStages::VERTEX; + bindings.insert(0, descriptor_set_layout_binding); + + let descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings, + ..Default::default() + }; + + let create_info = PipelineDescriptorSetLayoutCreateInfo { + set_layouts: vec![descriptor_set_layout], + flags: PipelineLayoutCreateFlags::default(), + push_constant_ranges: vec![], + } + .into_pipeline_layout_create_info(device.clone()) + .unwrap(); + + let layout = PipelineLayout::new(device.clone(), create_info).unwrap(); + + let subpass = PipelineRenderingCreateInfo { + color_attachment_formats: vec![Some(vulkano::format::Format::B8G8R8A8_UNORM)], + ..Default::default() + }; + + GraphicsPipeline::new( + device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap() + } + + fn get_pipeline(&self) -> &Arc { + &self.pipeline + } +} + +impl TrianglePipeline { + pub fn new(device: &Arc) -> Self { + Self { + pipeline: Self::create_pipeline(device), + } + } +} + +fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> { + let vs = shaders::vs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of vertex shader".to_string())?; + + let fs = shaders::fs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of fragment shader".to_string())?; + + Ok((vs, fs)) +} diff --git a/src/vulkan/renderer.rs b/src/vulkan/renderer.rs new file mode 100644 index 0000000..079276b --- /dev/null +++ b/src/vulkan/renderer.rs @@ -0,0 +1,167 @@ +use std::sync::Arc; +use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; +use vulkano::device::Queue; +use vulkano::image::view::ImageView; +use vulkano::pipeline::graphics::viewport::Viewport; +use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo}; +use vulkano::sync::{self, GpuFuture}; +use vulkano::{Validated, VulkanError}; +use winit::window::Window; + +pub struct VulkanRenderer { + pub window: Arc, + pub surface: Arc, + pub swapchain: Arc, + pub queue: Arc, + pub attachment_image_views: Vec>, + pub previous_frame_end: Option>, + pub recreate_swapchain: bool, + pub viewport: Viewport, +} + +impl VulkanRenderer { + pub fn new( + window: Arc, + surface: Arc, + device: Arc, + queue: Arc, + ) -> Self { + let window_size = window.inner_size(); + let surface_formats = device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap(); + let surface_format = surface_formats[0]; + + let (swapchain, images) = Swapchain::new( + device.clone(), + surface.clone(), + SwapchainCreateInfo { + image_extent: window_size.into(), + image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT, + image_format: surface_format.0, + ..Default::default() + }, + ) + .unwrap(); + + let attachment_image_views = images + .into_iter() + .map(|image| ImageView::new_default(image).unwrap()) + .collect(); + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + Self { + window, + surface, + swapchain, + queue, + attachment_image_views, + previous_frame_end: Some(sync::now(device).boxed()), + recreate_swapchain: false, + viewport, + } + } + + pub fn begin_frame(&mut self) -> Result<(u32, Box), VulkanError> { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swapchain { + self.recreate_swapchain(); + self.recreate_swapchain = false; + } + + let (image_index, suboptimal, acquire_future) = + match vulkano::swapchain::acquire_next_image(self.swapchain.clone(), None) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + self.recreate_swapchain = true; + return Err(VulkanError::OutOfDate); + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + self.recreate_swapchain = true; + } + + Ok((image_index, acquire_future.boxed())) + } + + pub fn end_frame( + &mut self, + image_index: u32, + acquire_future: Box, + command_buffer: AutoCommandBufferBuilder, + ) -> Result<(), VulkanError> { + let command_buffer = command_buffer.build().unwrap(); + let future = self + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present( + self.queue.clone(), + SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_index), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + self.previous_frame_end = Some(future.boxed()); + Ok(()) + } + Err(VulkanError::OutOfDate) => { + self.recreate_swapchain = true; + self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed()); + Ok(()) + } + Err(e) => { + println!("failed to flush future: {e}"); + self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed()); + Ok(()) + } + } + } + + fn recreate_swapchain(&mut self) { + let image_extent: [u32; 2] = self.window.inner_size().into(); + if image_extent.contains(&0) { + return; + } + + let surface_formats = self + .queue + .device() + .physical_device() + .surface_formats(&self.surface, Default::default()) + .unwrap(); + let surface_format = surface_formats[0]; + + let (new_swapchain, new_images) = self + .swapchain + .recreate(SwapchainCreateInfo { + image_extent, + image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT, + image_format: surface_format.0, + ..self.swapchain.create_info() + }) + .expect("failed to recreate swapchain"); + + self.swapchain = new_swapchain; + self.attachment_image_views = new_images + .into_iter() + .map(|image| ImageView::new_default(image).unwrap()) + .collect(); + self.viewport.extent = [image_extent[0] as f32, image_extent[1] as f32]; + } +} diff --git a/src/vulkan/resources/buffer.rs b/src/vulkan/resources/buffer.rs new file mode 100644 index 0000000..318d96e --- /dev/null +++ b/src/vulkan/resources/buffer.rs @@ -0,0 +1,65 @@ +use std::sync::Arc; +use vulkano::buffer::BufferContents; +use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; + +pub struct BufferManager { + memory_allocator: Arc, +} + +impl BufferManager { + pub fn new(memory_allocator: Arc) -> Self { + Self { memory_allocator } + } + + pub fn create_vertex_buffer(&self, data: &[T]) -> Subbuffer<[T]> { + Buffer::from_iter( + self.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + data.iter().cloned(), + ) + .unwrap() + } + + pub fn create_index_buffer(&self, data: &[u32]) -> Subbuffer<[u32]> { + Buffer::from_iter( + self.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::INDEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + data.iter().cloned(), + ) + .unwrap() + } + + pub fn create_uniform_buffer(&self, data: &T) -> Subbuffer { + Buffer::from_data( + self.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::UNIFORM_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + *data, + ) + .unwrap() + } +} diff --git a/src/vulkan/resources/descriptor.rs b/src/vulkan/resources/descriptor.rs new file mode 100644 index 0000000..6b7af8a --- /dev/null +++ b/src/vulkan/resources/descriptor.rs @@ -0,0 +1,47 @@ +use std::sync::Arc; +use vulkano::descriptor_set::DescriptorSet; +use vulkano::descriptor_set::WriteDescriptorSet; +use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; +use vulkano::descriptor_set::layout::{ + DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, +}; +use vulkano::device::Device; +use vulkano::shader::ShaderStages; + +pub struct DescriptorManager { + device: Arc, + allocator: Arc, +} + +impl DescriptorManager { + pub fn new(device: Arc, allocator: Arc) -> Self { + Self { device, allocator } + } + + pub fn create_descriptor_set_layout( + &self, + bindings: &[(u32, DescriptorType, u32)], + ) -> Arc { + let mut bindings_map = std::collections::BTreeMap::new(); + for (binding_index, ty, _count) in bindings { + let mut binding = DescriptorSetLayoutBinding::descriptor_type(*ty); + binding.stages = ShaderStages::all_graphics(); + bindings_map.insert(*binding_index, binding); + } + + let create_info = DescriptorSetLayoutCreateInfo { + bindings: bindings_map, + ..Default::default() + }; + + DescriptorSetLayout::new(self.device.clone(), create_info).unwrap() + } + + pub fn create_descriptor_set( + &self, + layout: &Arc, + writes: Vec, + ) -> Arc { + DescriptorSet::new(self.allocator.clone(), layout.clone(), writes, []).unwrap() + } +} diff --git a/src/vulkan/resources/mod.rs b/src/vulkan/resources/mod.rs new file mode 100644 index 0000000..0c2b51c --- /dev/null +++ b/src/vulkan/resources/mod.rs @@ -0,0 +1,3 @@ +pub mod buffer; +pub mod descriptor; +pub mod vertex; diff --git a/src/renderer/vertex.rs b/src/vulkan/resources/vertex.rs similarity index 82% rename from src/renderer/vertex.rs rename to src/vulkan/resources/vertex.rs index fc2ee21..cc25b1b 100644 --- a/src/renderer/vertex.rs +++ b/src/vulkan/resources/vertex.rs @@ -1,12 +1,12 @@ use std::sync::Arc; +use vulkano::Validated; use vulkano::buffer::{ AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, }; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::graphics::vertex_input::Vertex; -use vulkano::Validated; -#[derive(BufferContents, Vertex)] +#[derive(BufferContents, Vertex, Clone)] #[repr(C)] pub struct Vertex2D { #[format(R32G32_SFLOAT)] @@ -16,6 +16,14 @@ pub struct Vertex2D { pub color: [f32; 3], } +#[derive(BufferContents, Clone)] +#[repr(C)] +pub struct MVPData { + pub world: [[f32; 4]; 4], + pub view: [[f32; 4]; 4], + pub projection: [[f32; 4]; 4], +} + impl Vertex2D { pub fn create_buffer( vertices: Vec, @@ -32,7 +40,7 @@ impl Vertex2D { | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - vertices, + vertices.into_iter(), ) } } From 6bc3dbd53df570995c9145bd4daaa9773e56d76f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 1 Apr 2025 23:49:40 +0200 Subject: [PATCH 02/28] Migrate to bevy_ecs for more up to date ECS crates --- Cargo.lock | 550 ++++++++++++++++++++++++++++++++--------------------- Cargo.toml | 2 +- 2 files changed, 331 insertions(+), 221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e69aec8..e35561c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "const-random", "getrandom", "once_cell", "version_check", @@ -40,6 +41,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -117,48 +124,12 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "any_vec" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6ac04794a7749710e3c7f3c93222e3d04692993b69876d69393efd2565401a" - [[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" -[[package]] -name = "apecs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6df7760d4baebb17003dcf99134d8e3a63f487e146d58911f0bcd27afb185d1c" -dependencies = [ - "any_vec", - "apecs-derive", - "async-channel", - "itertools", - "log", - "moongraph", - "parking_lot", - "rayon", - "smallvec", - "snafu 0.8.5", -] - -[[package]] -name = "apecs-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0f3ddfd31fd5276fb8039b75dc4d284c21213757a969e480c6ef8fde494f3b" -dependencies = [ - "moongraph-macros-syntax", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "arrayref" version = "0.3.9" @@ -187,16 +158,35 @@ dependencies = [ ] [[package]] -name = "async-channel" -version = "1.9.0" +name = "assert_type_match" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", + "proc-macro2", + "quote", + "syn", ] +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -209,6 +199,127 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bevy_ecs" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1597106cc01e62e6217ccb662e0748b2ce330893f27c7dc17bac33e0bb99bca9" +dependencies = [ + "bevy_ecs_macros", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bitflags 2.9.0", + "concurrent-queue", + "derive_more", + "disqualified", + "fixedbitset 0.5.7", + "nonmax", + "petgraph", + "smallvec", +] + +[[package]] +name = "bevy_ecs_macros" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f453adf07712b39826bc5845e5b0887ce03204ee8359bbe6b40a9afda60564a1" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_macro_utils" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb6ded1ddc124ea214f6a2140e47a78d1fe79b0638dad39419cdeef2e1133f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_ptr" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89fe0b0b919146939481a3a7c38864face2c6d0fd2c73ab3d430dc693ecd9b11" + +[[package]] +name = "bevy_reflect" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ddbca0a39e88eff2c301dc794ee9d73a53f4b08d47b2c9b5a6aac182fae6217" +dependencies = [ + "assert_type_match", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "derive_more", + "disqualified", + "downcast-rs", + "erased-serde", + "serde", + "smallvec", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d62affb769db17d34ad0b75ff27eca94867e2acc8ea350c5eca97d102bd98709" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_tasks" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028630ddc355563bd567df1076db3515858aa26715ddf7467d2086f9b40e5ab1" +dependencies = [ + "async-executor", + "futures-channel", + "futures-lite", + "pin-project", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_utils" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63c2174d43a0de99f863c98a472370047a2bfa7d1e5cec8d9d647fb500905d9d" +dependencies = [ + "ahash", + "bevy_utils_proc_macros", + "getrandom", + "hashbrown 0.14.5", + "thread_local", + "tracing", + "web-time", +] + +[[package]] +name = "bevy_utils_proc_macros" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94847541f6dd2e28f54a9c2b0e857da5f2631e2201ebc25ce68781cdcb721391" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -230,17 +341,6 @@ dependencies = [ "objc2 0.5.2", ] -[[package]] -name = "broomdog" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ec65645d8167b03c07e049f114a878a11ab889f20c071d6f7b30bf88fbe5af" -dependencies = [ - "log", - "rustc-hash", - "snafu 0.8.5", -] - [[package]] name = "bumpalo" version = "3.17.0" @@ -264,7 +364,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -362,6 +462,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -402,25 +522,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -449,14 +550,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] -name = "dagga" -version = "0.2.1" +name = "derive_more" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf0d7dcd307c9c5d81277737c35d1faf08af9e2cb262966a01c91021686b68" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "log", - "rustc-hash", - "snafu 0.7.5", + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] @@ -465,6 +576,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "disqualified" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd" + [[package]] name = "dlib" version = "0.5.2" @@ -474,12 +591,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "downcast-rs" version = "1.2.1" @@ -492,12 +603,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "env_filter" version = "0.1.3" @@ -527,6 +632,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.10" @@ -538,10 +653,22 @@ dependencies = [ ] [[package]] -name = "event-listener" -version = "2.5.3" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "foldhash" @@ -567,7 +694,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -576,12 +703,40 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -599,8 +754,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -620,6 +777,17 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -645,7 +813,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", ] [[package]] @@ -654,15 +822,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -690,7 +849,7 @@ checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -804,30 +963,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "moongraph" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a4b09eb96a84205062b48ec5469c8c35c128167e838aa73dc620c4411af598" -dependencies = [ - "broomdog", - "dagga", - "log", - "rayon", - "snafu 0.8.5", -] - -[[package]] -name = "moongraph-macros-syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08112087acc92cc28fb5d8f7bda1307123ecc9a275ed4835f1c03f1a8dd02c1d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ndk" version = "0.9.0" @@ -868,6 +1003,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + [[package]] name = "num_enum" version = "0.7.3" @@ -886,7 +1027,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1170,6 +1311,12 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1199,6 +1346,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -1216,7 +1373,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1315,26 +1472,6 @@ dependencies = [ "objc2-quartz-core 0.3.0", ] -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1396,7 +1533,7 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", - "apecs", + "bevy_ecs", "env_logger", "glam", "log", @@ -1405,12 +1542,6 @@ dependencies = [ "winit", ] -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustix" version = "0.38.44" @@ -1487,7 +1618,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1584,66 +1715,12 @@ dependencies = [ "serde", ] -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive 0.7.5", -] - -[[package]] -name = "snafu" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" -dependencies = [ - "snafu-derive 0.8.5", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "snafu-derive" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.100" @@ -1672,7 +1749,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1685,6 +1762,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -1742,6 +1828,9 @@ name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] [[package]] name = "ttf-parser" @@ -1749,6 +1838,12 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -1761,12 +1856,27 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +dependencies = [ + "getrandom", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1823,7 +1933,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1837,7 +1947,7 @@ dependencies = [ "proc-macro2", "quote", "shaderc", - "syn 2.0.100", + "syn", "vulkano", ] @@ -1879,7 +1989,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn", "wasm-bindgen-shared", ] @@ -1914,7 +2024,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2418,5 +2528,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index ba7eb9d..a6ece63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ vulkano-shaders = "0.35" glam = { version = "0.30" } # ECS -apecs = "0.8" +bevy_ecs = "0.15.3" # Log and tracing log = "0.4" From f32db721011cf5ddb12f6e444b916fa62d8eaf0a Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 3 Apr 2025 18:38:17 +0200 Subject: [PATCH 03/28] rename renderer to vulkan --- src/main.rs | 4 ++-- src/{renderer => vulkan}/app.rs | 10 +++++----- src/{renderer => vulkan}/mod.rs | 0 src/{renderer => vulkan}/pipelines/mod.rs | 0 .../pipelines/triangle_pipeline.rs | 4 ++-- src/{renderer => vulkan}/render_context.rs | 0 src/{renderer => vulkan}/scene.rs | 4 ++-- src/{renderer => vulkan}/vertex.rs | 0 8 files changed, 11 insertions(+), 11 deletions(-) rename src/{renderer => vulkan}/app.rs (98%) rename src/{renderer => vulkan}/mod.rs (100%) rename src/{renderer => vulkan}/pipelines/mod.rs (100%) rename src/{renderer => vulkan}/pipelines/triangle_pipeline.rs (99%) rename src/{renderer => vulkan}/render_context.rs (100%) rename src/{renderer => vulkan}/scene.rs (96%) rename src/{renderer => vulkan}/vertex.rs (100%) diff --git a/src/main.rs b/src/main.rs index 6c01c40..e0ab6d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; -mod renderer; +mod vulkan; fn main() -> Result<(), impl Error> { env_logger::init(); @@ -9,7 +9,7 @@ fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let mut app = renderer::App::new(&event_loop); + let mut app = vulkan::App::new(&event_loop); event_loop.run_app(&mut app) } diff --git a/src/renderer/app.rs b/src/vulkan/app.rs similarity index 98% rename from src/renderer/app.rs rename to src/vulkan/app.rs index f791616..8a0397e 100644 --- a/src/renderer/app.rs +++ b/src/vulkan/app.rs @@ -1,8 +1,8 @@ -use crate::renderer::render_context::RenderContext; -use crate::renderer::Scene; +use crate::vulkan::Scene; +use crate::vulkan::render_context::RenderContext; use std::sync::Arc; -use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; use vulkano::buffer::BufferUsage; +use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, @@ -15,9 +15,9 @@ use vulkano::device::{ use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{acquire_next_image, Surface, SwapchainPresentInfo}; +use vulkano::swapchain::{Surface, SwapchainPresentInfo, acquire_next_image}; use vulkano::sync::GpuFuture; -use vulkano::{sync, Validated, Version, VulkanError, VulkanLibrary}; +use vulkano::{Validated, Version, VulkanError, VulkanLibrary, sync}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; diff --git a/src/renderer/mod.rs b/src/vulkan/mod.rs similarity index 100% rename from src/renderer/mod.rs rename to src/vulkan/mod.rs diff --git a/src/renderer/pipelines/mod.rs b/src/vulkan/pipelines/mod.rs similarity index 100% rename from src/renderer/pipelines/mod.rs rename to src/vulkan/pipelines/mod.rs diff --git a/src/renderer/pipelines/triangle_pipeline.rs b/src/vulkan/pipelines/triangle_pipeline.rs similarity index 99% rename from src/renderer/pipelines/triangle_pipeline.rs rename to src/vulkan/pipelines/triangle_pipeline.rs index f6001f7..0c1554d 100644 --- a/src/renderer/pipelines/triangle_pipeline.rs +++ b/src/vulkan/pipelines/triangle_pipeline.rs @@ -5,6 +5,7 @@ use vulkano::descriptor_set::layout::{ DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, }; use vulkano::device::Device; +use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; use vulkano::pipeline::graphics::multisample::MultisampleState; @@ -12,7 +13,6 @@ use vulkano::pipeline::graphics::rasterization::RasterizationState; use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; use vulkano::pipeline::graphics::viewport::ViewportState; -use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; use vulkano::pipeline::{ DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, @@ -20,7 +20,7 @@ use vulkano::pipeline::{ use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; -use crate::renderer::Vertex2D; +use crate::vulkan::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/renderer/render_context.rs b/src/vulkan/render_context.rs similarity index 100% rename from src/renderer/render_context.rs rename to src/vulkan/render_context.rs diff --git a/src/renderer/scene.rs b/src/vulkan/scene.rs similarity index 96% rename from src/renderer/scene.rs rename to src/vulkan/scene.rs index e3c6b58..83863e2 100644 --- a/src/renderer/scene.rs +++ b/src/vulkan/scene.rs @@ -1,4 +1,4 @@ -use crate::renderer::pipelines::triangle_pipeline::shaders::vs; +use crate::vulkan::pipelines::triangle_pipeline::shaders::vs; use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; @@ -8,7 +8,7 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use crate::renderer::{pipelines::triangle_pipeline::create_triangle_pipeline, App, Vertex2D}; +use crate::vulkan::{App, Vertex2D, pipelines::triangle_pipeline::create_triangle_pipeline}; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche diff --git a/src/renderer/vertex.rs b/src/vulkan/vertex.rs similarity index 100% rename from src/renderer/vertex.rs rename to src/vulkan/vertex.rs From 15c273b93d35b0f0cdc7fc3fac987c159f7362c8 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 3 Apr 2025 19:59:10 +0200 Subject: [PATCH 04/28] Split app, window render context and vulkan context --- src/core/app.rs | 178 +++++++++++ src/core/mod.rs | 1 + src/main.rs | 6 +- src/vulkan/app.rs | 295 ------------------ src/vulkan/mod.rs | 13 +- src/vulkan/pipelines/triangle_pipeline.rs | 2 +- src/vulkan/scene.rs | 35 ++- src/vulkan/vulkan_context.rs | 203 ++++++++++++ ...er_context.rs => window_render_context.rs} | 18 +- 9 files changed, 426 insertions(+), 325 deletions(-) create mode 100644 src/core/app.rs create mode 100644 src/core/mod.rs delete mode 100644 src/vulkan/app.rs create mode 100644 src/vulkan/vulkan_context.rs rename src/vulkan/{render_context.rs => window_render_context.rs} (89%) diff --git a/src/core/app.rs b/src/core/app.rs new file mode 100644 index 0000000..93a7d56 --- /dev/null +++ b/src/core/app.rs @@ -0,0 +1,178 @@ +use crate::vulkan::scene::Scene; +use crate::vulkan::vulkan_context::VulkanContext; +use crate::vulkan::window_render_context::WindowRenderContext; +use std::sync::Arc; +use vulkano::command_buffer::{RenderingAttachmentInfo, RenderingInfo}; +use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; +use vulkano::swapchain::{SwapchainPresentInfo, acquire_next_image}; +use vulkano::sync::GpuFuture; +use vulkano::{Validated, VulkanError, sync}; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::ActiveEventLoop; +use winit::window::WindowId; + +pub struct App { + vulkan_context: VulkanContext, + window_render_context: Option, + scene: Option, +} + +impl From for App { + fn from(vulkan_context: VulkanContext) -> Self { + Self { + vulkan_context, + window_render_context: None, + scene: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_attributes = winit::window::Window::default_attributes() + .with_title("Rust ASH Test") + .with_inner_size(winit::dpi::PhysicalSize::new( + f64::from(800), + f64::from(600), + )); + + let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); + + let surface = self.vulkan_context.create_surface(window.clone()); + + self.window_render_context = Some(WindowRenderContext::new( + window, + surface, + &self.vulkan_context.device, + )); + self.scene = Some( + Scene::load( + &self.vulkan_context, + self.window_render_context.as_ref().unwrap(), + ) + .unwrap(), + ); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + WindowEvent::Resized(_) => { + let rcx = self.window_render_context.as_mut().unwrap(); + rcx.recreate_swapchain = true; + } + WindowEvent::RedrawRequested => { + let (image_index, acquire_future) = { + let rcx = self.window_render_context.as_mut().unwrap(); + let window_size = rcx.window.inner_size(); + + if window_size.width == 0 || window_size.height == 0 { + return; + } + + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.update_swapchain().unwrap(); + + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(rcx.swapchain.clone(), None) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + rcx.recreate_swapchain = true; + } + + (image_index, acquire_future) + }; + + let mut builder = self.vulkan_context.create_render_builder(); + + { + let rcx = self.window_render_context.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, 1.0].into()), + ..RenderingAttachmentInfo::image_view( + rcx.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) + .unwrap(); + } + + if let Some(scene) = self.scene.as_ref() { + scene + .render( + &self.vulkan_context, + &self.window_render_context.as_ref().unwrap(), + &mut builder, + ) + .unwrap(); + } + + builder.end_rendering().unwrap(); + + let command_buffer = builder.build().unwrap(); + + { + let rcx = self.window_render_context.as_mut().unwrap(); + + let future = rcx + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.vulkan_context.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present( + self.vulkan_context.graphics_queue.clone(), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + rcx.previous_frame_end = Some(future.boxed()); + } + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + rcx.previous_frame_end = + Some(sync::now(self.vulkan_context.device.clone()).boxed()); + } + Err(e) => { + println!("failed to flush future: {e}"); + rcx.previous_frame_end = + Some(sync::now(self.vulkan_context.device.clone()).boxed()); + } + } + } + } + _ => {} + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.window_render_context.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..309be62 --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1 @@ +pub mod app; diff --git a/src/main.rs b/src/main.rs index e0ab6d4..54a1427 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; -mod vulkan; +pub mod core; +pub mod vulkan; fn main() -> Result<(), impl Error> { env_logger::init(); @@ -9,7 +10,8 @@ fn main() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let mut app = vulkan::App::new(&event_loop); + let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); + let mut app = core::app::App::from(vulkan_context); event_loop.run_app(&mut app) } diff --git a/src/vulkan/app.rs b/src/vulkan/app.rs deleted file mode 100644 index 8a0397e..0000000 --- a/src/vulkan/app.rs +++ /dev/null @@ -1,295 +0,0 @@ -use crate::vulkan::Scene; -use crate::vulkan::render_context::RenderContext; -use std::sync::Arc; -use vulkano::buffer::BufferUsage; -use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; -use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; -use vulkano::command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo, -}; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; -use vulkano::device::physical::PhysicalDeviceType; -use vulkano::device::{ - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, -}; -use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; -use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; -use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::{Surface, SwapchainPresentInfo, acquire_next_image}; -use vulkano::sync::GpuFuture; -use vulkano::{Validated, Version, VulkanError, VulkanLibrary, sync}; -use winit::application::ApplicationHandler; -use winit::event::WindowEvent; -use winit::event_loop::{ActiveEventLoop, EventLoop}; -use winit::window::WindowId; - -pub struct App { - pub instance: Arc, - pub device: Arc, - pub queue: Arc, - - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub uniform_buffer_allocator: SubbufferAllocator, - pub descriptor_set_allocator: Arc, - - pub rcx: Option, - scene: Option, -} - -impl App { - pub fn new(event_loop: &EventLoop<()>) -> Self { - let library = VulkanLibrary::new().unwrap(); - - for layer in library.layer_properties().unwrap() { - log::debug!("Available layer: {}", layer.name()); - } - - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - - let instance = 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(); - - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, queue_family_index) = 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| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .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, - }) - .expect("no suitable physical device found"); - - 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); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, - ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - ..Default::default() - }, - ) - .unwrap(); - - let 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 uniform_buffer_allocator = SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - ); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - - Self { - instance, - device, - queue, - memory_allocator, - command_buffer_allocator, - uniform_buffer_allocator, - descriptor_set_allocator, - rcx: None, - scene: None, - } - } -} - -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window_attributes = winit::window::Window::default_attributes() - .with_title("Rust ASH Test") - .with_inner_size(winit::dpi::PhysicalSize::new( - f64::from(800), - f64::from(600), - )); - - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - - self.rcx = Some(RenderContext::new(window, surface, &self.device)); - self.scene = Some(Scene::load(&self).unwrap()); - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => { - log::debug!("The close button was pressed; stopping"); - event_loop.exit(); - } - WindowEvent::Resized(_) => { - let rcx = self.rcx.as_mut().unwrap(); - rcx.recreate_swapchain = true; - } - WindowEvent::RedrawRequested => { - let (image_index, acquire_future) = { - let rcx = self.rcx.as_mut().unwrap(); - let window_size = rcx.window.inner_size(); - - if window_size.width == 0 || window_size.height == 0 { - return; - } - - rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - rcx.update_swapchain().unwrap(); - - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - if suboptimal { - rcx.recreate_swapchain = true; - } - - (image_index, acquire_future) - }; - - let mut builder = AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); - - { - let rcx = self.rcx.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, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - rcx.attachment_image_views[image_index as usize].clone(), - ) - })], - ..Default::default() - }) - .unwrap() - .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) - .unwrap(); - } - - if let Some(scene) = self.scene.as_ref() { - scene.render(&self, &mut builder).unwrap(); - } - - builder.end_rendering().unwrap(); - - let command_buffer = builder.build().unwrap(); - - { - let rcx = self.rcx.as_mut().unwrap(); - - let future = rcx - .previous_frame_end - .take() - .unwrap() - .join(acquire_future) - .then_execute(self.queue.clone(), command_buffer) - .unwrap() - .then_swapchain_present( - self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index( - rcx.swapchain.clone(), - image_index, - ), - ) - .then_signal_fence_and_flush(); - - match future.map_err(Validated::unwrap) { - Ok(future) => { - rcx.previous_frame_end = Some(future.boxed()); - } - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - } - Err(e) => { - println!("failed to flush future: {e}"); - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); - } - } - } - } - _ => {} - } - } - - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let rcx = self.rcx.as_mut().unwrap(); - rcx.window.request_redraw(); - } -} diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index bbcff9a..56d8c07 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,9 +1,6 @@ -mod app; -mod pipelines; -mod render_context; -mod vertex; -pub use app::App; +pub mod pipelines; +pub mod vertex; +pub mod vulkan_context; +pub mod window_render_context; -mod scene; -pub use scene::Scene; -pub use vertex::Vertex2D; +pub mod scene; diff --git a/src/vulkan/pipelines/triangle_pipeline.rs b/src/vulkan/pipelines/triangle_pipeline.rs index 0c1554d..bb07389 100644 --- a/src/vulkan/pipelines/triangle_pipeline.rs +++ b/src/vulkan/pipelines/triangle_pipeline.rs @@ -20,7 +20,7 @@ use vulkano::pipeline::{ use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; -use crate::vulkan::Vertex2D; +use crate::vulkan::vertex::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/vulkan/scene.rs b/src/vulkan/scene.rs index 83863e2..1f77e5a 100644 --- a/src/vulkan/scene.rs +++ b/src/vulkan/scene.rs @@ -8,7 +8,10 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use crate::vulkan::{App, Vertex2D, pipelines::triangle_pipeline::create_triangle_pipeline}; +use crate::vulkan::{pipelines::triangle_pipeline::create_triangle_pipeline, vertex::Vertex2D}; + +use super::vulkan_context::VulkanContext; +use super::window_render_context::WindowRenderContext; const VERTICES: [Vertex2D; 12] = [ // Triangle en haut à gauche @@ -73,10 +76,14 @@ pub struct Scene { } impl Scene { - pub fn load(app: &App) -> Result> { - let pipeline = create_triangle_pipeline(&app.device, &app.rcx.as_ref().unwrap().swapchain)?; + pub fn load( + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, + ) -> Result> { + let pipeline = + create_triangle_pipeline(&vulkan_context.device, &window_render_context.swapchain)?; let vertex_buffer = - Vertex2D::create_buffer(Vec::from_iter(VERTICES), &app.memory_allocator)?; + Vertex2D::create_buffer(Vec::from_iter(VERTICES), &vulkan_context.memory_allocator)?; Ok(Scene { pipeline, @@ -87,16 +94,17 @@ impl Scene { pub fn render( &self, - app: &App, + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, builder: &mut AutoCommandBufferBuilder, ) -> Result<(), Box> { let vertex_count = self.vertex_buffer.len() as u32; let instance_count = vertex_count / 3; - let uniform_buffer = self.get_uniform_buffer(app); + let uniform_buffer = self.get_uniform_buffer(vulkan_context, window_render_context); let layout = &self.pipeline.layout().set_layouts()[0]; let descriptor_set = DescriptorSet::new( - app.descriptor_set_allocator.clone(), + vulkan_context.descriptor_set_allocator.clone(), layout.clone(), [WriteDescriptorSet::buffer(0, uniform_buffer)], [], @@ -119,8 +127,12 @@ impl Scene { Ok(()) } - fn get_uniform_buffer(&self, app: &App) -> Subbuffer { - let swapchain = &app.rcx.as_ref().unwrap().swapchain; + fn get_uniform_buffer( + &self, + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, + ) -> Subbuffer { + let swapchain = &window_render_context.swapchain; let elapsed = self.rotation_start.elapsed(); let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; let rotation = Mat3::from_rotation_y(rotation as f32); @@ -143,7 +155,10 @@ impl Scene { projection: proj.to_cols_array_2d(), }; - let buffer = app.uniform_buffer_allocator.allocate_sized().unwrap(); + let buffer = vulkan_context + .uniform_buffer_allocator + .allocate_sized() + .unwrap(); *buffer.write().unwrap() = uniform_data; buffer diff --git a/src/vulkan/vulkan_context.rs b/src/vulkan/vulkan_context.rs new file mode 100644 index 0000000..ef2c6f7 --- /dev/null +++ b/src/vulkan/vulkan_context.rs @@ -0,0 +1,203 @@ +use std::{any::Any, sync::Arc}; + +use vulkano::{ + Version, VulkanLibrary, + buffer::{ + BufferUsage, + allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, + }, + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + allocator::StandardCommandBufferAllocator, + }, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, + QueueFlags, physical::PhysicalDeviceType, + }, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}, + swapchain::Surface, +}; +use winit::{ + event_loop::EventLoop, + raw_window_handle::{HasDisplayHandle, HasWindowHandle}, +}; + +pub struct VulkanContext { + instance: Arc, + pub device: Arc, + pub graphics_queue: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub uniform_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +impl From<&EventLoop<()>> for VulkanContext { + fn from(event_loop: &EventLoop<()>) -> Self { + let library = load_library(); + + let enabled_extensions = Surface::required_extensions(event_loop).unwrap(); + + let instance = create_instance(library.clone(), enabled_extensions); + + let (device, mut queues) = pick_graphics_device(&instance, event_loop); + 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 uniform_buffer_allocator = Arc::new(SubbufferAllocator::new( + memory_allocator.clone(), + SubbufferAllocatorCreateInfo { + buffer_usage: BufferUsage::UNIFORM_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + )); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + + Self { + instance, + device, + graphics_queue, + memory_allocator, + command_buffer_allocator, + uniform_buffer_allocator, + descriptor_set_allocator, + } + } +} + +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() + } +} + +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 +} + +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() +} + +fn pick_graphics_device( + instance: &Arc, + event_loop: &EventLoop<()>, +) -> ( + Arc, + impl ExactSizeIterator> + use<>, +) { + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + let (physical_device, queue_family_index) = 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| { + p.queue_family_properties() + .iter() + .enumerate() + .position(|(i, q)| { + q.queue_flags.intersects(QueueFlags::GRAPHICS) + && p.presentation_support(i as u32, event_loop).unwrap() + }) + .map(|i| (p, i as u32)) + }) + .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, + }) + .expect("no suitable physical device found"); + + 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, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + ..Default::default() + }, + ) + .unwrap() +} diff --git a/src/vulkan/render_context.rs b/src/vulkan/window_render_context.rs similarity index 89% rename from src/vulkan/render_context.rs rename to src/vulkan/window_render_context.rs index dd7e840..54120d0 100644 --- a/src/vulkan/render_context.rs +++ b/src/vulkan/window_render_context.rs @@ -5,19 +5,19 @@ use vulkano::image::{Image, ImageUsage}; use vulkano::pipeline::graphics::viewport::Viewport; use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use vulkano::sync::GpuFuture; -use vulkano::{sync, Validated, VulkanError}; +use vulkano::{Validated, VulkanError, sync}; use winit::window::Window; -pub struct RenderContext { - pub(super) window: Arc, - pub(super) swapchain: Arc, - pub(super) attachment_image_views: Vec>, - pub(super) viewport: Viewport, - pub(super) recreate_swapchain: bool, - pub(super) previous_frame_end: Option>, +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 RenderContext { +impl WindowRenderContext { pub fn new(window: Arc, surface: Arc, device: &Arc) -> Self { let window_size = window.inner_size(); From 2fbf4e6ce2ff41a2234fda87da49439a35f9191b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 3 Apr 2025 21:10:08 +0200 Subject: [PATCH 05/28] Split pick_graphics_device --- src/vulkan/vulkan_context.rs | 81 +++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/vulkan/vulkan_context.rs b/src/vulkan/vulkan_context.rs index ef2c6f7..ea1b29b 100644 --- a/src/vulkan/vulkan_context.rs +++ b/src/vulkan/vulkan_context.rs @@ -13,7 +13,8 @@ use vulkano::{ descriptor_set::allocator::StandardDescriptorSetAllocator, device::{ Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, - QueueFlags, physical::PhysicalDeviceType, + QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}, @@ -133,6 +134,53 @@ fn create_instance( .unwrap() } +fn find_physical_device_queue_family_indexes( + physical_device: &Arc, + event_loop: &EventLoop<()>, +) -> 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, event_loop) + .unwrap() + { + graphic_queue_family_index = Some(i as u32); + } + } + + graphic_queue_family_index +} + +fn pick_physical_device_and_queue_family_indexes( + instance: &Arc, + event_loop: &EventLoop<()>, + 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, event_loop) + .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, + }) +} + fn pick_graphics_device( instance: &Arc, event_loop: &EventLoop<()>, @@ -145,32 +193,9 @@ fn pick_graphics_device( ..DeviceExtensions::empty() }; - let (physical_device, queue_family_index) = 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| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(i, q)| { - q.queue_flags.intersects(QueueFlags::GRAPHICS) - && p.presentation_support(i as u32, event_loop).unwrap() - }) - .map(|i| (p, i as u32)) - }) - .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, - }) - .expect("no suitable physical device found"); + let (physical_device, graphics_family_index) = + pick_physical_device_and_queue_family_indexes(instance, event_loop, &device_extensions) + .unwrap(); log::debug!( "Using device: {} (type: {:?})", @@ -188,7 +213,7 @@ fn pick_graphics_device( physical_device, DeviceCreateInfo { queue_create_infos: vec![QueueCreateInfo { - queue_family_index, + queue_family_index: graphics_family_index, ..Default::default() }], enabled_extensions: device_extensions, From 852d72d716710ac0e9c30298d26371c7d98bdbf5 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 4 Apr 2025 13:38:27 +0200 Subject: [PATCH 06/28] Begin move mesh + Vertex and Camera into core --- src/core/camera/mod.rs | 19 +++++++++++++++++++ src/core/mod.rs | 3 +++ src/core/render/mesh.rs | 14 ++++++++++++++ src/core/render/mod.rs | 2 ++ src/{vulkan => core/render}/vertex.rs | 0 src/core/scene.rs | 21 +++++++++++++++++++++ src/game/mod.rs | 1 + src/main.rs | 1 + src/vulkan/mod.rs | 1 - src/vulkan/pipelines/triangle_pipeline.rs | 2 +- src/vulkan/scene.rs | 3 ++- 11 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/core/camera/mod.rs create mode 100644 src/core/render/mesh.rs create mode 100644 src/core/render/mod.rs rename src/{vulkan => core/render}/vertex.rs (100%) create mode 100644 src/core/scene.rs create mode 100644 src/game/mod.rs diff --git a/src/core/camera/mod.rs b/src/core/camera/mod.rs new file mode 100644 index 0000000..f9b6925 --- /dev/null +++ b/src/core/camera/mod.rs @@ -0,0 +1,19 @@ +use bevy_ecs::component::Component; +use glam::{Mat4, Quat, Vec3}; + +pub trait Camera: Into + Component {} + +#[derive(Component)] +pub struct Camera3D { + pub projection: Mat4, + pub position: Vec3, + pub rotation: Quat, +} + +impl Into for Camera3D { + fn into(self) -> Mat4 { + Mat4::from_rotation_translation(self.rotation, self.position) * self.projection + } +} + +impl Camera for Camera3D {} diff --git a/src/core/mod.rs b/src/core/mod.rs index 309be62..f058532 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1 +1,4 @@ pub mod app; +pub mod camera; +pub mod render; +pub mod scene; diff --git a/src/core/render/mesh.rs b/src/core/render/mesh.rs new file mode 100644 index 0000000..ca5ec0f --- /dev/null +++ b/src/core/render/mesh.rs @@ -0,0 +1,14 @@ +use bevy_ecs::component::Component; + +use super::vertex::Vertex2D; + +#[derive(Component)] +pub struct Mesh2D { + pub vertices: Vec, +} + +impl Mesh2D { + pub fn new(vertices: Vec) -> Self { + Self { vertices } + } +} diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs new file mode 100644 index 0000000..6f29814 --- /dev/null +++ b/src/core/render/mod.rs @@ -0,0 +1,2 @@ +pub mod mesh; +pub mod vertex; diff --git a/src/vulkan/vertex.rs b/src/core/render/vertex.rs similarity index 100% rename from src/vulkan/vertex.rs rename to src/core/render/vertex.rs diff --git a/src/core/scene.rs b/src/core/scene.rs new file mode 100644 index 0000000..a424da3 --- /dev/null +++ b/src/core/scene.rs @@ -0,0 +1,21 @@ +use bevy_ecs::world::World; + +pub struct Scene { + world: World, +} + +impl Scene { + pub fn new() -> Self { + Self { + world: World::new(), + } + } + + pub fn world(&self) -> &World { + &self.world + } + + pub fn world_mut(&mut self) -> &mut World { + &mut self.world + } +} diff --git a/src/game/mod.rs b/src/game/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/game/mod.rs @@ -0,0 +1 @@ + diff --git a/src/main.rs b/src/main.rs index 54a1427..2b80274 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; pub mod core; +pub mod game; pub mod vulkan; fn main() -> Result<(), impl Error> { diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 56d8c07..dc3d731 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,5 +1,4 @@ pub mod pipelines; -pub mod vertex; pub mod vulkan_context; pub mod window_render_context; diff --git a/src/vulkan/pipelines/triangle_pipeline.rs b/src/vulkan/pipelines/triangle_pipeline.rs index bb07389..e573747 100644 --- a/src/vulkan/pipelines/triangle_pipeline.rs +++ b/src/vulkan/pipelines/triangle_pipeline.rs @@ -20,7 +20,7 @@ use vulkano::pipeline::{ use vulkano::shader::{EntryPoint, ShaderStages}; use vulkano::swapchain::Swapchain; -use crate::vulkan::vertex::Vertex2D; +use crate::core::render::vertex::Vertex2D; pub mod shaders { pub mod vs { diff --git a/src/vulkan/scene.rs b/src/vulkan/scene.rs index 1f77e5a..4d1de95 100644 --- a/src/vulkan/scene.rs +++ b/src/vulkan/scene.rs @@ -8,7 +8,8 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -use crate::vulkan::{pipelines::triangle_pipeline::create_triangle_pipeline, vertex::Vertex2D}; +use crate::core::render::vertex::Vertex2D; +use crate::vulkan::pipelines::triangle_pipeline::create_triangle_pipeline; use super::vulkan_context::VulkanContext; use super::window_render_context::WindowRenderContext; From 9664ea754ca4a0cda52a76603391699e9ce9fad7 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 7 Apr 2025 13:11:19 +0200 Subject: [PATCH 07/28] flake: Fix missing libstdc++ --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 565976b..4f7cd2e 100644 --- a/flake.nix +++ b/flake.nix @@ -39,6 +39,8 @@ buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ + stdenv.cc.cc.lib + # Wayland libxkbcommon wayland From 1d333b633b19e0691a760e504df4cc17185bf392 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 7 Apr 2025 17:03:00 +0200 Subject: [PATCH 08/28] Push lunch break code --- src/core/render/material/mod.rs | 10 ++++++++ src/core/render/material/triangle.rs | 34 ++++++++++++++++++++++++++++ src/core/render/mod.rs | 1 + 3 files changed, 45 insertions(+) create mode 100644 src/core/render/material/mod.rs create mode 100644 src/core/render/material/triangle.rs diff --git a/src/core/render/material/mod.rs b/src/core/render/material/mod.rs new file mode 100644 index 0000000..8cccde2 --- /dev/null +++ b/src/core/render/material/mod.rs @@ -0,0 +1,10 @@ +pub mod triangle; + +use vulkano::command_buffer::CommandBuffer; + +use crate::vulkan::vulkan_context::VulkanContext; + +pub trait Material { + fn load(&self, vulkan_context: &VulkanContext); + fn bind(&self, command_buffer: &mut CommandBuffer); +} diff --git a/src/core/render/material/triangle.rs b/src/core/render/material/triangle.rs new file mode 100644 index 0000000..c3bafaa --- /dev/null +++ b/src/core/render/material/triangle.rs @@ -0,0 +1,34 @@ +use glam::Vec4; +use vulkano::command_buffer::CommandBuffer; + +use super::Material; + +pub mod shaders { + pub mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/vertex.vert", + } + } + + pub mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/vertex.frag", + } + } +} + +pub struct TriangleMaterial { + pub colors: [Vec4; 3], +} + +impl Material for TriangleMaterial { + fn bind(&self, command_buffer: &mut CommandBuffer) { + todo!() + } + + fn load(&self, vulkan_context: &crate::vulkan::vulkan_context::VulkanContext) { + todo!() + } +} diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs index 6f29814..5d003a8 100644 --- a/src/core/render/mod.rs +++ b/src/core/render/mod.rs @@ -1,2 +1,3 @@ +pub mod material; pub mod mesh; pub mod vertex; From b361965033337551f488e39f4bd81b945abf4754 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 7 Apr 2025 22:51:49 +0200 Subject: [PATCH 09/28] Update --- src/core/render/material.rs | 7 ++++++ src/core/render/material/mod.rs | 10 -------- src/core/render/material/triangle.rs | 34 ---------------------------- src/core/render/vertex.rs | 2 +- src/vulkan/scene.rs | 24 +++++++++++++------- src/vulkan/vulkan_context.rs | 18 +-------------- 6 files changed, 25 insertions(+), 70 deletions(-) create mode 100644 src/core/render/material.rs delete mode 100644 src/core/render/material/mod.rs delete mode 100644 src/core/render/material/triangle.rs diff --git a/src/core/render/material.rs b/src/core/render/material.rs new file mode 100644 index 0000000..539d4bc --- /dev/null +++ b/src/core/render/material.rs @@ -0,0 +1,7 @@ +use std::sync::Arc; + +use bevy_ecs::component::Component; +use vulkano::pipeline::GraphicsPipeline; + +#[derive(Component)] +pub struct Material(pub Arc); diff --git a/src/core/render/material/mod.rs b/src/core/render/material/mod.rs deleted file mode 100644 index 8cccde2..0000000 --- a/src/core/render/material/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod triangle; - -use vulkano::command_buffer::CommandBuffer; - -use crate::vulkan::vulkan_context::VulkanContext; - -pub trait Material { - fn load(&self, vulkan_context: &VulkanContext); - fn bind(&self, command_buffer: &mut CommandBuffer); -} diff --git a/src/core/render/material/triangle.rs b/src/core/render/material/triangle.rs deleted file mode 100644 index c3bafaa..0000000 --- a/src/core/render/material/triangle.rs +++ /dev/null @@ -1,34 +0,0 @@ -use glam::Vec4; -use vulkano::command_buffer::CommandBuffer; - -use super::Material; - -pub mod shaders { - pub mod vs { - vulkano_shaders::shader! { - ty: "vertex", - path: r"res/shaders/vertex.vert", - } - } - - pub mod fs { - vulkano_shaders::shader! { - ty: "fragment", - path: r"res/shaders/vertex.frag", - } - } -} - -pub struct TriangleMaterial { - pub colors: [Vec4; 3], -} - -impl Material for TriangleMaterial { - fn bind(&self, command_buffer: &mut CommandBuffer) { - todo!() - } - - fn load(&self, vulkan_context: &crate::vulkan::vulkan_context::VulkanContext) { - todo!() - } -} diff --git a/src/core/render/vertex.rs b/src/core/render/vertex.rs index fc2ee21..9bf133e 100644 --- a/src/core/render/vertex.rs +++ b/src/core/render/vertex.rs @@ -1,10 +1,10 @@ use std::sync::Arc; +use vulkano::Validated; use vulkano::buffer::{ AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, }; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::graphics::vertex_input::Vertex; -use vulkano::Validated; #[derive(BufferContents, Vertex)] #[repr(C)] diff --git a/src/vulkan/scene.rs b/src/vulkan/scene.rs index 4d1de95..22986ff 100644 --- a/src/vulkan/scene.rs +++ b/src/vulkan/scene.rs @@ -3,9 +3,10 @@ use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; use std::time::Instant; -use vulkano::buffer::Subbuffer; +use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use crate::core::render::vertex::Vertex2D; @@ -156,12 +157,19 @@ impl Scene { projection: proj.to_cols_array_2d(), }; - let buffer = vulkan_context - .uniform_buffer_allocator - .allocate_sized() - .unwrap(); - *buffer.write().unwrap() = uniform_data; - - buffer + Buffer::from_data( + vulkan_context.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::UNIFORM_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + uniform_data, + ) + .unwrap() } } diff --git a/src/vulkan/vulkan_context.rs b/src/vulkan/vulkan_context.rs index ea1b29b..bad47de 100644 --- a/src/vulkan/vulkan_context.rs +++ b/src/vulkan/vulkan_context.rs @@ -2,10 +2,6 @@ use std::{any::Any, sync::Arc}; use vulkano::{ Version, VulkanLibrary, - buffer::{ - BufferUsage, - allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, - }, command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, allocator::StandardCommandBufferAllocator, @@ -17,7 +13,7 @@ use vulkano::{ physical::{PhysicalDevice, PhysicalDeviceType}, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, - memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}, + memory::allocator::StandardMemoryAllocator, swapchain::Surface, }; use winit::{ @@ -32,7 +28,6 @@ pub struct VulkanContext { pub memory_allocator: Arc, pub command_buffer_allocator: Arc, - pub uniform_buffer_allocator: Arc, pub descriptor_set_allocator: Arc, } @@ -54,16 +49,6 @@ impl From<&EventLoop<()>> for VulkanContext { Default::default(), )); - let uniform_buffer_allocator = Arc::new(SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - )); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( device.clone(), Default::default(), @@ -75,7 +60,6 @@ impl From<&EventLoop<()>> for VulkanContext { graphics_queue, memory_allocator, command_buffer_allocator, - uniform_buffer_allocator, descriptor_set_allocator, } } From f4694157ab53d3223089ad0032964dd67ca500da Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 16:35:21 +0200 Subject: [PATCH 10/28] Update --- src/core/app/app.rs | 54 +++++++++++++++++++++++++++++++ src/core/app/mod.rs | 4 +++ src/core/app/plugin.rs | 5 +++ src/core/mod.rs | 2 ++ src/core/{app.rs => old_app.rs} | 0 src/core/window/mod.rs | 24 ++++++++++++++ src/core/window/window_handler.rs | 40 +++++++++++++++++++++++ src/main.rs | 17 ++++++++-- 8 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 src/core/app/app.rs create mode 100644 src/core/app/mod.rs create mode 100644 src/core/app/plugin.rs rename src/core/{app.rs => old_app.rs} (100%) create mode 100644 src/core/window/mod.rs create mode 100644 src/core/window/window_handler.rs diff --git a/src/core/app/app.rs b/src/core/app/app.rs new file mode 100644 index 0000000..12b4be7 --- /dev/null +++ b/src/core/app/app.rs @@ -0,0 +1,54 @@ +use std::error::Error; + +use bevy_ecs::{schedule::Schedules, world::World}; + +pub enum AppExit { + Success, + Error(Box), +} + +pub type RunnerFn = Box AppExit>; + +pub struct App { + world: World, + runner: Option, +} + +impl Default for App { + fn default() -> Self { + let mut world = World::new(); + world.init_resource::(); + + Self { + world, + runner: None, + } + } +} + +impl App { + pub fn world_mut(&mut self) -> &mut World { + &mut self.world + } + + pub fn world(&self) -> &World { + &self.world + } + + pub fn run(&mut self) -> Result<(), Box> { + match self.runner.take() { + Some(runner) => match runner() { + AppExit::Success => Ok(()), + AppExit::Error(e) => Err(e), + }, + None => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "runner is not set", + ))), + } + } + + pub fn set_runner(&mut self, runner: RunnerFn) { + self.runner = Some(runner); + } +} diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs new file mode 100644 index 0000000..f7320aa --- /dev/null +++ b/src/core/app/mod.rs @@ -0,0 +1,4 @@ +mod app; +pub mod plugin; + +pub use app::App; diff --git a/src/core/app/plugin.rs b/src/core/app/plugin.rs new file mode 100644 index 0000000..987c540 --- /dev/null +++ b/src/core/app/plugin.rs @@ -0,0 +1,5 @@ +use super::app::App; + +pub trait Plugin { + fn build(&self, app: &mut App); +} diff --git a/src/core/mod.rs b/src/core/mod.rs index f058532..07427ac 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,6 @@ pub mod app; pub mod camera; +pub mod old_app; pub mod render; pub mod scene; +pub mod window; diff --git a/src/core/app.rs b/src/core/old_app.rs similarity index 100% rename from src/core/app.rs rename to src/core/old_app.rs diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs new file mode 100644 index 0000000..55f6a70 --- /dev/null +++ b/src/core/window/mod.rs @@ -0,0 +1,24 @@ +pub mod window_handler; + +use super::app::{App, plugin::Plugin}; +use window_handler::WindowHandler; +use winit::event_loop::EventLoop; +use winit::window::WindowAttributes; + +pub struct WindowPlugin { + window_attributes: WindowAttributes, + event_loop: EventLoop<()>, +} + +impl Plugin for WindowPlugin { + fn build(&self, app: &mut App) { + let world = app.world_mut(); + world.insert_resource(WindowHandler::new(self.window_attributes.clone())); + + let window_handler = world.get_resource_mut::().unwrap(); + + // app.set_runner(Box::new(move || { + // self.event_loop.run_app(&mut window_handler); + // })); + } +} diff --git a/src/core/window/window_handler.rs b/src/core/window/window_handler.rs new file mode 100644 index 0000000..2b87d35 --- /dev/null +++ b/src/core/window/window_handler.rs @@ -0,0 +1,40 @@ +use bevy_ecs::system::Resource; +use winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::ActiveEventLoop, + window::{Window, WindowAttributes, WindowId}, +}; + +#[derive(Resource)] +pub struct WindowHandler { + window_attributes: WindowAttributes, + window: Option>, +} + +impl WindowHandler { + pub fn new(window_attributes: WindowAttributes) -> Self { + Self { + window_attributes, + window: None, + } + } +} +impl ApplicationHandler for WindowHandler { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = event_loop + .create_window(self.window_attributes.clone()) + .unwrap(); + self.window = Some(Box::new(window)); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + _ => {} + } + } +} diff --git a/src/main.rs b/src/main.rs index 2b80274..f09236f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,25 @@ pub mod core; pub mod game; pub mod vulkan; -fn main() -> Result<(), impl Error> { +fn main() -> Result<(), Box> { env_logger::init(); + run_old_app() +} + +fn run_new_app() -> Result<(), Box> { + let mut app = core::app::App::default(); + app.run() +} + +fn run_old_app() -> Result<(), Box> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); - let mut app = core::app::App::from(vulkan_context); + let mut app = core::old_app::App::from(vulkan_context); - event_loop.run_app(&mut app) + event_loop.run_app(&mut app).map_err(Box::new)?; + + Ok(()) } From df99ef3a3f1641f4f753bb67de76f99c288532fc Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 16:49:07 +0200 Subject: [PATCH 11/28] Add AppError --- Cargo.lock | 33 +++++++++++++++++++++++++++------ Cargo.toml | 1 + src/core/app/app.rs | 21 +++++++++++++-------- src/main.rs | 14 ++++++-------- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e35561c..da7454f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -384,7 +384,7 @@ dependencies = [ "polling", "rustix", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -863,7 +863,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -975,7 +975,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1537,6 +1537,7 @@ dependencies = [ "env_logger", "glam", "log", + "thiserror 2.0.12", "vulkano", "vulkano-shaders", "winit", @@ -1695,7 +1696,7 @@ dependencies = [ "log", "memmap2", "rustix", - "thiserror", + "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -1738,7 +1739,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -1752,6 +1762,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" diff --git a/Cargo.toml b/Cargo.toml index a6ece63..a84f681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] anyhow = "1.0" +thiserror = "2.0" winit = { version = "0.30", features = ["rwh_06"] } vulkano = "0.35" diff --git a/src/core/app/app.rs b/src/core/app/app.rs index 12b4be7..21ebd28 100644 --- a/src/core/app/app.rs +++ b/src/core/app/app.rs @@ -7,7 +7,15 @@ pub enum AppExit { Error(Box), } -pub type RunnerFn = Box AppExit>; +pub type RunnerFn = Box AppExit>; + +#[derive(Debug, thiserror::Error)] +pub enum AppError { + #[error("Runner is not set")] + RunnerNotSet, + #[error("Runner returned an error : {0}")] + RunnerError(Box), +} pub struct App { world: World, @@ -35,16 +43,13 @@ impl App { &self.world } - pub fn run(&mut self) -> Result<(), Box> { + pub fn run(mut self) -> Result<(), AppError> { match self.runner.take() { - Some(runner) => match runner() { + Some(runner) => match runner(self) { AppExit::Success => Ok(()), - AppExit::Error(e) => Err(e), + AppExit::Error(e) => Err(AppError::RunnerError(e)), }, - None => Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "runner is not set", - ))), + None => Err(AppError::RunnerNotSet), } } diff --git a/src/main.rs b/src/main.rs index f09236f..9412fad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,25 +5,23 @@ pub mod core; pub mod game; pub mod vulkan; -fn main() -> Result<(), Box> { +fn main() -> Result<(), impl Error> { env_logger::init(); - run_old_app() + run_new_app() } -fn run_new_app() -> Result<(), Box> { - let mut app = core::app::App::default(); +fn run_new_app() -> Result<(), impl Error> { + let app = core::app::App::default(); app.run() } -fn run_old_app() -> Result<(), Box> { +fn run_old_app() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); let mut app = core::old_app::App::from(vulkan_context); - event_loop.run_app(&mut app).map_err(Box::new)?; - - Ok(()) + event_loop.run_app(&mut app) } From 4f6216635f7fe88577f6e05b624ceb6a19589a5b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 18:06:18 +0200 Subject: [PATCH 12/28] Update --- src/core/app/mod.rs | 4 +- src/core/app/plugin.rs | 5 --- src/core/mod.rs | 2 - src/core/scene.rs | 21 --------- src/core/window/config.rs | 17 +++++++ src/core/window/mod.rs | 37 ++++++++------- src/core/window/state.rs | 45 +++++++++++++++++++ src/core/window/window_handler.rs | 40 ----------------- src/game/mod.rs | 13 ++++++ src/main.rs | 12 ++--- src/{core/old_app.rs => old_app/app.rs} | 6 +-- src/{vulkan => old_app}/mod.rs | 4 +- src/{vulkan => old_app}/pipelines/mod.rs | 0 .../pipelines/triangle_pipeline.rs | 0 src/{vulkan => old_app}/scene.rs | 4 +- src/{vulkan => old_app}/vulkan_context.rs | 0 .../window_render_context.rs | 0 17 files changed, 110 insertions(+), 100 deletions(-) delete mode 100644 src/core/app/plugin.rs delete mode 100644 src/core/scene.rs create mode 100644 src/core/window/config.rs create mode 100644 src/core/window/state.rs delete mode 100644 src/core/window/window_handler.rs rename src/{core/old_app.rs => old_app/app.rs} (97%) rename src/{vulkan => old_app}/mod.rs (87%) rename src/{vulkan => old_app}/pipelines/mod.rs (100%) rename src/{vulkan => old_app}/pipelines/triangle_pipeline.rs (100%) rename src/{vulkan => old_app}/scene.rs (97%) rename src/{vulkan => old_app}/vulkan_context.rs (100%) rename src/{vulkan => old_app}/window_render_context.rs (100%) diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs index f7320aa..c6c8a20 100644 --- a/src/core/app/mod.rs +++ b/src/core/app/mod.rs @@ -1,4 +1,2 @@ mod app; -pub mod plugin; - -pub use app::App; +pub use app::*; diff --git a/src/core/app/plugin.rs b/src/core/app/plugin.rs deleted file mode 100644 index 987c540..0000000 --- a/src/core/app/plugin.rs +++ /dev/null @@ -1,5 +0,0 @@ -use super::app::App; - -pub trait Plugin { - fn build(&self, app: &mut App); -} diff --git a/src/core/mod.rs b/src/core/mod.rs index 07427ac..bd7ce34 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,6 +1,4 @@ pub mod app; pub mod camera; -pub mod old_app; pub mod render; -pub mod scene; pub mod window; diff --git a/src/core/scene.rs b/src/core/scene.rs deleted file mode 100644 index a424da3..0000000 --- a/src/core/scene.rs +++ /dev/null @@ -1,21 +0,0 @@ -use bevy_ecs::world::World; - -pub struct Scene { - world: World, -} - -impl Scene { - pub fn new() -> Self { - Self { - world: World::new(), - } - } - - pub fn world(&self) -> &World { - &self.world - } - - pub fn world_mut(&mut self) -> &mut World { - &mut self.world - } -} diff --git a/src/core/window/config.rs b/src/core/window/config.rs new file mode 100644 index 0000000..8fbef8c --- /dev/null +++ b/src/core/window/config.rs @@ -0,0 +1,17 @@ +use bevy_ecs::system::Resource; +use winit::{dpi::PhysicalSize, window::WindowAttributes}; + +#[derive(Resource, Clone)] +pub struct WindowConfig { + pub title: String, + pub width: u32, + pub height: u32, +} + +impl Into for &WindowConfig { + fn into(self) -> WindowAttributes { + WindowAttributes::default() + .with_title(self.title.clone()) + .with_inner_size(PhysicalSize::new(self.width as f64, self.height as f64)) + } +} diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index 55f6a70..e600120 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,24 +1,27 @@ -pub mod window_handler; - -use super::app::{App, plugin::Plugin}; -use window_handler::WindowHandler; +use config::WindowConfig; +use state::WindowState; use winit::event_loop::EventLoop; -use winit::window::WindowAttributes; -pub struct WindowPlugin { - window_attributes: WindowAttributes, - event_loop: EventLoop<()>, +use super::app::{App, AppExit}; + +pub mod config; +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))); } -impl Plugin for WindowPlugin { - fn build(&self, app: &mut App) { - let world = app.world_mut(); - world.insert_resource(WindowHandler::new(self.window_attributes.clone())); +fn runner(app: App, event_loop: EventLoop<()>) -> AppExit { + let mut window_state = WindowState::new(app); - let window_handler = world.get_resource_mut::().unwrap(); - - // app.set_runner(Box::new(move || { - // self.event_loop.run_app(&mut window_handler); - // })); + match event_loop.run_app(&mut window_state) { + Ok(_) => AppExit::Success, + Err(e) => AppExit::Error(Box::new(e)), } } diff --git a/src/core/window/state.rs b/src/core/window/state.rs new file mode 100644 index 0000000..94bfcc7 --- /dev/null +++ b/src/core/window/state.rs @@ -0,0 +1,45 @@ +use bevy_ecs::world::World; +use winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::ActiveEventLoop, + window::{Window, WindowId}, +}; + +use crate::core::app::App; + +use super::config::WindowConfig; + +pub struct WindowState { + app: App, + window: Option, +} + +impl WindowState { + pub fn new(app: App) -> Self { + Self { app, window: None } + } + + fn world(&self) -> &World { + self.app.world() + } +} + +impl ApplicationHandler for WindowState { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_config = self.world().get_resource::().unwrap(); + + let window = event_loop.create_window(window_config.into()).unwrap(); + self.window = Some(window); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + _ => {} + } + } +} diff --git a/src/core/window/window_handler.rs b/src/core/window/window_handler.rs deleted file mode 100644 index 2b87d35..0000000 --- a/src/core/window/window_handler.rs +++ /dev/null @@ -1,40 +0,0 @@ -use bevy_ecs::system::Resource; -use winit::{ - application::ApplicationHandler, - event::WindowEvent, - event_loop::ActiveEventLoop, - window::{Window, WindowAttributes, WindowId}, -}; - -#[derive(Resource)] -pub struct WindowHandler { - window_attributes: WindowAttributes, - window: Option>, -} - -impl WindowHandler { - pub fn new(window_attributes: WindowAttributes) -> Self { - Self { - window_attributes, - window: None, - } - } -} -impl ApplicationHandler for WindowHandler { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window = event_loop - .create_window(self.window_attributes.clone()) - .unwrap(); - self.window = Some(Box::new(window)); - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => { - log::debug!("The close button was pressed; stopping"); - event_loop.exit(); - } - _ => {} - } - } -} diff --git a/src/game/mod.rs b/src/game/mod.rs index 8b13789..ffee10f 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1 +1,14 @@ +use crate::core::{ + app::App, + window::{self, config::WindowConfig}, +}; +pub fn init(app: &mut App) { + let window_config = WindowConfig { + title: "Rust ASH Test".to_string(), + width: 800, + height: 600, + }; + + window::init(app, window_config); +} diff --git a/src/main.rs b/src/main.rs index 9412fad..c981713 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,16 +3,18 @@ use winit::event_loop::{ControlFlow, EventLoop}; pub mod core; pub mod game; -pub mod vulkan; +pub mod old_app; fn main() -> Result<(), impl Error> { env_logger::init(); - run_new_app() + // run_new_app() + run_old_app() } fn run_new_app() -> Result<(), impl Error> { - let app = core::app::App::default(); + let mut app = core::app::App::default(); + game::init(&mut app); app.run() } @@ -20,8 +22,8 @@ fn run_old_app() -> Result<(), impl Error> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); - let vulkan_context = vulkan::vulkan_context::VulkanContext::from(&event_loop); - let mut app = core::old_app::App::from(vulkan_context); + let vulkan_context = old_app::vulkan_context::VulkanContext::from(&event_loop); + let mut app = old_app::app::App::from(vulkan_context); event_loop.run_app(&mut app) } diff --git a/src/core/old_app.rs b/src/old_app/app.rs similarity index 97% rename from src/core/old_app.rs rename to src/old_app/app.rs index 93a7d56..cfcf038 100644 --- a/src/core/old_app.rs +++ b/src/old_app/app.rs @@ -1,6 +1,6 @@ -use crate::vulkan::scene::Scene; -use crate::vulkan::vulkan_context::VulkanContext; -use crate::vulkan::window_render_context::WindowRenderContext; +use crate::old_app::scene::Scene; +use crate::old_app::vulkan_context::VulkanContext; +use crate::old_app::window_render_context::WindowRenderContext; use std::sync::Arc; use vulkano::command_buffer::{RenderingAttachmentInfo, RenderingInfo}; use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; diff --git a/src/vulkan/mod.rs b/src/old_app/mod.rs similarity index 87% rename from src/vulkan/mod.rs rename to src/old_app/mod.rs index dc3d731..9fce0a9 100644 --- a/src/vulkan/mod.rs +++ b/src/old_app/mod.rs @@ -1,5 +1,5 @@ +pub mod app; pub mod pipelines; +pub mod scene; pub mod vulkan_context; pub mod window_render_context; - -pub mod scene; diff --git a/src/vulkan/pipelines/mod.rs b/src/old_app/pipelines/mod.rs similarity index 100% rename from src/vulkan/pipelines/mod.rs rename to src/old_app/pipelines/mod.rs diff --git a/src/vulkan/pipelines/triangle_pipeline.rs b/src/old_app/pipelines/triangle_pipeline.rs similarity index 100% rename from src/vulkan/pipelines/triangle_pipeline.rs rename to src/old_app/pipelines/triangle_pipeline.rs diff --git a/src/vulkan/scene.rs b/src/old_app/scene.rs similarity index 97% rename from src/vulkan/scene.rs rename to src/old_app/scene.rs index 22986ff..72e3f48 100644 --- a/src/vulkan/scene.rs +++ b/src/old_app/scene.rs @@ -1,4 +1,4 @@ -use crate::vulkan::pipelines::triangle_pipeline::shaders::vs; +use crate::old_app::pipelines::triangle_pipeline::shaders::vs; use glam::{Mat3, Mat4, Vec3}; use std::error::Error; use std::sync::Arc; @@ -10,7 +10,7 @@ use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; use crate::core::render::vertex::Vertex2D; -use crate::vulkan::pipelines::triangle_pipeline::create_triangle_pipeline; +use crate::old_app::pipelines::triangle_pipeline::create_triangle_pipeline; use super::vulkan_context::VulkanContext; use super::window_render_context::WindowRenderContext; diff --git a/src/vulkan/vulkan_context.rs b/src/old_app/vulkan_context.rs similarity index 100% rename from src/vulkan/vulkan_context.rs rename to src/old_app/vulkan_context.rs diff --git a/src/vulkan/window_render_context.rs b/src/old_app/window_render_context.rs similarity index 100% rename from src/vulkan/window_render_context.rs rename to src/old_app/window_render_context.rs From e2616a0ef5af2ae8ff0d1e351017976f97c7138a Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 18:45:33 +0200 Subject: [PATCH 13/28] Add vulkan creation from resources --- src/core/mod.rs | 1 + src/core/vulkan/context.rs | 65 ++++++++++++++++ src/core/vulkan/mod.rs | 24 ++++++ src/core/vulkan/utils.rs | 137 ++++++++++++++++++++++++++++++++++ src/core/window/mod.rs | 39 +++++++--- src/core/window/raw_handle.rs | 18 +++++ src/game/mod.rs | 6 +- src/main.rs | 4 +- 8 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 src/core/vulkan/context.rs create mode 100644 src/core/vulkan/mod.rs create mode 100644 src/core/vulkan/utils.rs create mode 100644 src/core/window/raw_handle.rs 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> { From a04c7694389af64c1e716f29949aa9b8155b786b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 19:23:05 +0200 Subject: [PATCH 14/28] Begin add Window Render Context --- src/core/vulkan/mod.rs | 10 +- .../vulkan/{context.rs => vulkan_context.rs} | 26 +++- src/core/vulkan/window_render_context.rs | 117 ++++++++++++++++++ src/core/window/raw_handle.rs | 7 +- src/core/window/state.rs | 17 +-- 5 files changed, 163 insertions(+), 14 deletions(-) rename src/core/vulkan/{context.rs => vulkan_context.rs} (69%) create mode 100644 src/core/vulkan/window_render_context.rs 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) { From 8b0c59f7c0682a0391a9527033e56174640dc57f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Apr 2025 20:05:17 +0200 Subject: [PATCH 15/28] Remove useless schedule (for now) --- src/core/app/app.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/app/app.rs b/src/core/app/app.rs index 21ebd28..3821bfc 100644 --- a/src/core/app/app.rs +++ b/src/core/app/app.rs @@ -1,6 +1,6 @@ use std::error::Error; -use bevy_ecs::{schedule::Schedules, world::World}; +use bevy_ecs::world::World; pub enum AppExit { Success, @@ -24,11 +24,8 @@ pub struct App { impl Default for App { fn default() -> Self { - let mut world = World::new(); - world.init_resource::(); - Self { - world, + world: World::new(), runner: None, } } From a295093c97252f86c86aa624f225ab98eb1c9443 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 24 Apr 2025 13:05:38 +0200 Subject: [PATCH 16/28] Use bevy_app instead --- Cargo.lock | 63 ++++++++++++++++++++++++ Cargo.toml | 5 +- src/core/app/app.rs | 56 --------------------- src/core/app/mod.rs | 2 - src/core/mod.rs | 1 - src/core/vulkan/mod.rs | 2 +- src/core/vulkan/vulkan_context.rs | 3 +- src/core/vulkan/window_render_context.rs | 2 +- src/core/window/mod.rs | 8 +-- src/core/window/state.rs | 3 +- src/game/mod.rs | 3 +- src/main.rs | 29 +++++++---- 12 files changed, 98 insertions(+), 79 deletions(-) delete mode 100644 src/core/app/app.rs delete mode 100644 src/core/app/mod.rs diff --git a/Cargo.lock b/Cargo.lock index da7454f..9e91469 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,6 +199,36 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bevy_app" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ac033a388b8699d241499a43783a09e6a3bab2430f1297c6bd4974095efb3f" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "console_error_panic_hook", + "ctrlc", + "derive_more", + "downcast-rs", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "bevy_derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d94761ce947b0a2402fd949fe1e7a5b1535293130ba4cd9893be6295d4680a" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn", +] + [[package]] name = "bevy_ecs" version = "0.15.3" @@ -462,6 +492,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const-random" version = "0.1.18" @@ -543,6 +583,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +[[package]] +name = "ctrlc" +version = "3.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" +dependencies = [ + "nix", + "windows-sys 0.59.0", +] + [[package]] name = "cursor-icon" version = "1.1.0" @@ -993,6 +1043,18 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -1533,6 +1595,7 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", + "bevy_app", "bevy_ecs", "env_logger", "glam", diff --git a/Cargo.toml b/Cargo.toml index a84f681..4542e45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,9 @@ vulkano-shaders = "0.35" glam = { version = "0.30" } # ECS -bevy_ecs = "0.15.3" +bevy_ecs = "0.15" +bevy_app = "0.15" # Log and tracing log = "0.4" -env_logger = "0.11.5" +env_logger = "0.11" diff --git a/src/core/app/app.rs b/src/core/app/app.rs deleted file mode 100644 index 3821bfc..0000000 --- a/src/core/app/app.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::error::Error; - -use bevy_ecs::world::World; - -pub enum AppExit { - Success, - Error(Box), -} - -pub type RunnerFn = Box AppExit>; - -#[derive(Debug, thiserror::Error)] -pub enum AppError { - #[error("Runner is not set")] - RunnerNotSet, - #[error("Runner returned an error : {0}")] - RunnerError(Box), -} - -pub struct App { - world: World, - runner: Option, -} - -impl Default for App { - fn default() -> Self { - Self { - world: World::new(), - runner: None, - } - } -} - -impl App { - pub fn world_mut(&mut self) -> &mut World { - &mut self.world - } - - pub fn world(&self) -> &World { - &self.world - } - - pub fn run(mut self) -> Result<(), AppError> { - match self.runner.take() { - Some(runner) => match runner(self) { - AppExit::Success => Ok(()), - AppExit::Error(e) => Err(AppError::RunnerError(e)), - }, - None => Err(AppError::RunnerNotSet), - } - } - - pub fn set_runner(&mut self, runner: RunnerFn) { - self.runner = Some(runner); - } -} diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs deleted file mode 100644 index c6c8a20..0000000 --- a/src/core/app/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod app; -pub use app::*; diff --git a/src/core/mod.rs b/src/core/mod.rs index abaeeae..db2061f 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,3 @@ -pub mod app; pub mod camera; pub mod render; pub mod vulkan; diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs index fe22fb3..29fe224 100644 --- a/src/core/vulkan/mod.rs +++ b/src/core/vulkan/mod.rs @@ -1,7 +1,7 @@ use vulkan_context::VulkanContext; use window_render_context::WindowRenderContext; -use super::app::App; +use bevy_app::App; mod utils; mod vulkan_context; diff --git a/src/core/vulkan/vulkan_context.rs b/src/core/vulkan/vulkan_context.rs index f59d9a3..b8be6ca 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -1,5 +1,6 @@ use std::{any::Any, sync::Arc}; +use bevy_app::App; use bevy_ecs::system::Resource; use vulkano::{ command_buffer::{ @@ -14,7 +15,7 @@ use vulkano::{ }; use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use crate::core::{app::App, window::raw_handle::DisplayHandleWrapper}; +use crate::core::window::raw_handle::DisplayHandleWrapper; use super::utils; diff --git a/src/core/vulkan/window_render_context.rs b/src/core/vulkan/window_render_context.rs index 1c653a7..5cb2618 100644 --- a/src/core/vulkan/window_render_context.rs +++ b/src/core/vulkan/window_render_context.rs @@ -1,3 +1,4 @@ +use bevy_app::App; use bevy_ecs::system::Resource; use std::sync::Arc; use vulkano::image::view::ImageView; @@ -8,7 +9,6 @@ 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; diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index 4368310..d1623b7 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,10 +1,9 @@ +use bevy_app::{App, AppExit}; 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; @@ -43,6 +42,9 @@ fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { match event_loop.run_app(&mut window_state) { Ok(_) => AppExit::Success, - Err(e) => AppExit::Error(Box::new(e)), + Err(e) => { + log::error!("Error running window state: {e}"); + AppExit::error() + } } } diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 0b0966e..8ffd44b 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,13 +1,12 @@ use std::sync::Arc; +use bevy_app::App; use bevy_ecs::world::World; use winit::{ application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId, }; -use crate::core::app::App; - use super::{config::WindowConfig, raw_handle::WindowWrapper}; pub struct WindowState { diff --git a/src/game/mod.rs b/src/game/mod.rs index 0b7787d..6eb9cab 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,5 +1,6 @@ +use bevy_app::App; + use crate::core::{ - app::App, vulkan::Vulkan, window::{Window, config::WindowConfig}, }; diff --git a/src/main.rs b/src/main.rs index 8297797..34fad4b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,40 @@ -use std::error::Error; use winit::event_loop::{ControlFlow, EventLoop}; +use bevy_app::{App, AppExit}; + pub mod core; pub mod game; pub mod old_app; -fn main() -> Result<(), impl Error> { +fn main() { env_logger::init(); - run_new_app() - // run_old_app() + run_new_app(); + // run_old_app(); } -fn run_new_app() -> Result<(), impl Error> { - let mut app = core::app::App::default(); +fn run_new_app() { + let mut app = App::default(); game::init(&mut app); - app.run() + match app.run() { + AppExit::Success => {} + AppExit::Error(e) => { + log::error!("Error running new app: {e}"); + } + } } -fn run_old_app() -> Result<(), impl Error> { +fn run_old_app() { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let vulkan_context = old_app::vulkan_context::VulkanContext::from(&event_loop); let mut app = old_app::app::App::from(vulkan_context); - event_loop.run_app(&mut app) + match event_loop.run_app(&mut app) { + Ok(_) => {} + Err(e) => { + log::error!("Error running old app: {e}"); + } + } } From 285b1942803429e675ece2eb8144bf2b7e858fac Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 27 Apr 2025 17:16:50 +0200 Subject: [PATCH 17/28] Surface: Add required extensions --- src/core/vulkan/vulkan_context.rs | 1 + src/old_app/vulkan_context.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/core/vulkan/vulkan_context.rs b/src/core/vulkan/vulkan_context.rs index b8be6ca..74df916 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -58,6 +58,7 @@ impl From<&App> for VulkanContext { world.get_resource::().unwrap(); let enabled_extensions = Surface::required_extensions(&display_handle.0).unwrap(); + log::debug!("Surface required extensions: {enabled_extensions:?}"); let instance = utils::create_instance(library.clone(), enabled_extensions); diff --git a/src/old_app/vulkan_context.rs b/src/old_app/vulkan_context.rs index bad47de..332dbcf 100644 --- a/src/old_app/vulkan_context.rs +++ b/src/old_app/vulkan_context.rs @@ -36,6 +36,7 @@ impl From<&EventLoop<()>> for VulkanContext { let library = load_library(); let enabled_extensions = Surface::required_extensions(event_loop).unwrap(); + log::debug!("Surface required extensions: {enabled_extensions:?}"); let instance = create_instance(library.clone(), enabled_extensions); From dda368e8023506a090e62e59ee6378bf3e6880da Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 16 May 2025 13:47:24 +0200 Subject: [PATCH 18/28] Update all dependencies --- Cargo.lock | 689 ++++++++++++++--------- Cargo.toml | 4 +- flake.lock | 12 +- flake.nix | 10 +- rust-toolchain.toml | 2 +- src/core/vulkan/vulkan_context.rs | 2 +- src/core/vulkan/window_render_context.rs | 2 +- src/core/window/config.rs | 2 +- src/core/window/raw_handle.rs | 2 +- 9 files changed, 426 insertions(+), 299 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e91469..e9d2554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,12 +20,11 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "const-random", "getrandom", "once_cell", "version_check", @@ -41,12 +40,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android-activity" version = "0.6.0" @@ -54,7 +47,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.0", + "bitflags 2.9.1", "cc", "cesu8", "jni", @@ -126,9 +119,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arrayref" @@ -170,14 +163,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -186,12 +180,18 @@ name = "async-task" version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +dependencies = [ + "portable-atomic", +] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +dependencies = [ + "portable-atomic", +] [[package]] name = "autocfg" @@ -201,28 +201,29 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bevy_app" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ac033a388b8699d241499a43783a09e6a3bab2430f1297c6bd4974095efb3f" +checksum = "a2b6267ac23a9947d5b2725ff047a1e1add70076d85fa9fb73d044ab9bea1f3c" dependencies = [ "bevy_derive", "bevy_ecs", + "bevy_platform", "bevy_reflect", "bevy_tasks", "bevy_utils", - "console_error_panic_hook", + "cfg-if", "ctrlc", - "derive_more", - "downcast-rs", - "wasm-bindgen", - "web-sys", + "downcast-rs 2.0.1", + "log", + "thiserror 2.0.12", + "variadics_please", ] [[package]] name = "bevy_derive" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d94761ce947b0a2402fd949fe1e7a5b1535293130ba4cd9893be6295d4680a" +checksum = "f626531b9c05c25a758ede228727bd11c2c2c8498ecbed9925044386d525a2a3" dependencies = [ "bevy_macro_utils", "quote", @@ -231,30 +232,37 @@ dependencies = [ [[package]] name = "bevy_ecs" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1597106cc01e62e6217ccb662e0748b2ce330893f27c7dc17bac33e0bb99bca9" +checksum = "d9e807b5d9aab3bb8dfe47e7a44c9ff088bad2ceefe299b80ac77609a87fe9d4" dependencies = [ + "arrayvec", "bevy_ecs_macros", + "bevy_platform", "bevy_ptr", "bevy_reflect", "bevy_tasks", "bevy_utils", - "bitflags 2.9.0", + "bitflags 2.9.1", + "bumpalo", "concurrent-queue", "derive_more", "disqualified", - "fixedbitset 0.5.7", + "fixedbitset", + "indexmap", + "log", "nonmax", - "petgraph", + "serde", "smallvec", + "thiserror 2.0.12", + "variadics_please", ] [[package]] name = "bevy_ecs_macros" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f453adf07712b39826bc5845e5b0887ce03204ee8359bbe6b40a9afda60564a1" +checksum = "467d7bb98aeb8dd30f36e6a773000c12a891d4f1bee2adc3841ec89cc8eaf54e" dependencies = [ "bevy_macro_utils", "proc-macro2", @@ -264,10 +272,11 @@ dependencies = [ [[package]] name = "bevy_macro_utils" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb6ded1ddc124ea214f6a2140e47a78d1fe79b0638dad39419cdeef2e1133f1" +checksum = "7a2473db70d8785b5c75d6dd951a2e51e9be2c2311122db9692c79c9d887517b" dependencies = [ + "parking_lot", "proc-macro2", "quote", "syn", @@ -275,34 +284,58 @@ dependencies = [ ] [[package]] -name = "bevy_ptr" -version = "0.15.3" +name = "bevy_platform" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89fe0b0b919146939481a3a7c38864face2c6d0fd2c73ab3d430dc693ecd9b11" +checksum = "704db2c11b7bc31093df4fbbdd3769f9606a6a5287149f4b51f2680f25834ebc" +dependencies = [ + "cfg-if", + "critical-section", + "foldhash", + "hashbrown", + "portable-atomic", + "portable-atomic-util", + "serde", + "spin", +] + +[[package]] +name = "bevy_ptr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f1275dfb4cfef4ffc90c3fa75408964864facf833acc932413d52aa5364ba4" [[package]] name = "bevy_reflect" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ddbca0a39e88eff2c301dc794ee9d73a53f4b08d47b2c9b5a6aac182fae6217" +checksum = "607ebacc31029cf2f39ac330eabf1d4bc411b159528ec08dbe6b0593eaccfd41" dependencies = [ "assert_type_match", + "bevy_platform", "bevy_ptr", "bevy_reflect_derive", "bevy_utils", "derive_more", "disqualified", - "downcast-rs", + "downcast-rs 2.0.1", "erased-serde", + "foldhash", + "glam 0.29.3", "serde", "smallvec", + "smol_str", + "thiserror 2.0.12", + "uuid", + "variadics_please", + "wgpu-types", ] [[package]] name = "bevy_reflect_derive" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d62affb769db17d34ad0b75ff27eca94867e2acc8ea350c5eca97d102bd98709" +checksum = "cf35e45e4eb239018369f63f2adc2107a54c329f9276d020e01eee1625b0238b" dependencies = [ "bevy_macro_utils", "proc-macro2", @@ -313,41 +346,29 @@ dependencies = [ [[package]] name = "bevy_tasks" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028630ddc355563bd567df1076db3515858aa26715ddf7467d2086f9b40e5ab1" +checksum = "444c450b65e108855f42ecb6db0c041a56ea7d7f10cc6222f0ca95e9536a7d19" dependencies = [ "async-executor", - "futures-channel", + "async-task", + "atomic-waker", + "bevy_platform", + "cfg-if", + "crossbeam-queue", + "derive_more", "futures-lite", - "pin-project", - "wasm-bindgen-futures", + "heapless", ] [[package]] name = "bevy_utils" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c2174d43a0de99f863c98a472370047a2bfa7d1e5cec8d9d647fb500905d9d" +checksum = "ac2da3b3c1f94dadefcbe837aaa4aa119fcea37f7bdc5307eb05b4ede1921e24" dependencies = [ - "ahash", - "bevy_utils_proc_macros", - "getrandom", - "hashbrown 0.14.5", + "bevy_platform", "thread_local", - "tracing", - "web-time", -] - -[[package]] -name = "bevy_utils_proc_macros" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94847541f6dd2e28f54a9c2b0e857da5f2631e2201ebc25ce68781cdcb721391" -dependencies = [ - "proc-macro2", - "quote", - "syn", ] [[package]] @@ -358,9 +379,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] [[package]] name = "block2" @@ -379,24 +403,30 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.10.1" @@ -409,7 +439,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "log", "polling", "rustix", @@ -431,9 +461,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "jobserver", "libc", @@ -490,36 +520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", + "portable-atomic", ] [[package]] @@ -562,6 +563,12 @@ dependencies = [ "libc", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -585,9 +592,9 @@ checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "ctrlc" -version = "3.4.6" +version = "3.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" dependencies = [ "nix", "windows-sys 0.59.0", @@ -626,6 +633,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", +] + [[package]] name = "disqualified" version = "1.0.0" @@ -648,10 +665,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] -name = "dpi" -version = "0.1.1" +name = "downcast-rs" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "env_filter" @@ -665,9 +688,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -694,9 +717,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -708,12 +731,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "fixedbitset" version = "0.5.7" @@ -722,9 +739,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" @@ -753,15 +770,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" version = "0.3.31" @@ -799,28 +807,36 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", - "js-sys", "libc", + "r-efi", "wasi", - "wasm-bindgen", ] [[package]] name = "glam" -version = "0.30.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17fcdf9683c406c2fc4d124afd29c0d595e22210d633cbdb8695ba9935ab1dc6" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" +dependencies = [ + "serde", +] + +[[package]] +name = "glam" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b46b9ca4690308844c644e7c634d68792467260e051c8543e0c7871662b3ba7" [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "bytemuck", "cfg-if", @@ -828,21 +844,34 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.14.5" +name = "hash32" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ - "ahash", - "allocator-api2", - "serde", + "byteorder", ] [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "equivalent", + "serde", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "portable-atomic", + "stable_deref_trait", +] [[package]] name = "heck" @@ -858,12 +887,12 @@ checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown", ] [[package]] @@ -880,9 +909,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.4" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -893,9 +922,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.4" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -926,10 +955,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom", "libc", ] @@ -945,18 +975,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] @@ -965,9 +995,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", - "redox_syscall 0.5.10", + "redox_syscall 0.5.12", ] [[package]] @@ -988,9 +1018,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" @@ -1019,7 +1049,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "jni-sys", "log", "ndk-sys", @@ -1045,11 +1075,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", @@ -1110,9 +1140,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" dependencies = [ "objc2-encode", ] @@ -1123,7 +1153,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "libc", "objc2 0.5.2", @@ -1139,7 +1169,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-core-location", @@ -1163,7 +1193,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1171,12 +1201,13 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.1", + "dispatch2", + "objc2 0.6.1", ] [[package]] @@ -1215,7 +1246,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "dispatch", "libc", @@ -1224,12 +1255,12 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.1", + "objc2 0.6.1", "objc2-core-foundation", ] @@ -1251,7 +1282,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1259,13 +1290,13 @@ dependencies = [ [[package]] name = "objc2-metal" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c41bc8b0e50ea7a5304a56f25e0066f526e99641b46fd7b9ad4421dd35bff6" +checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-foundation 0.3.1", ] [[package]] @@ -1274,7 +1305,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1283,15 +1314,15 @@ dependencies = [ [[package]] name = "objc2-quartz-core" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", + "bitflags 2.9.1", + "objc2 0.6.1", "objc2-core-foundation", - "objc2-foundation 0.3.0", - "objc2-metal 0.3.0", + "objc2-foundation 0.3.1", + "objc2-metal 0.3.1", ] [[package]] @@ -1310,7 +1341,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-cloud-kit", @@ -1342,7 +1373,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-core-location", @@ -1351,9 +1382,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "orbclient" @@ -1397,7 +1428,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.10", + "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] @@ -1408,16 +1439,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset 0.4.2", - "indexmap", -] - [[package]] name = "pin-project" version = "1.1.10" @@ -1491,18 +1512,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.37.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -1516,6 +1537,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -1528,10 +1555,10 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.1", "objc2-core-foundation", - "objc2-foundation 0.3.0", - "objc2-quartz-core 0.3.0", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", ] [[package]] @@ -1545,11 +1572,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -1598,7 +1625,7 @@ dependencies = [ "bevy_app", "bevy_ecs", "env_logger", - "glam", + "glam 0.30.3", "log", "thiserror 2.0.12", "vulkano", @@ -1612,7 +1639,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -1741,9 +1768,9 @@ checksum = "9db491c0d4152a069911a0fbdaca959691bf0b9d7110d98a7ed1c8e59b79ab30" [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smithay-client-toolkit" @@ -1751,7 +1778,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "calloop", "calloop-wayland-source", "cursor-icon", @@ -1779,6 +1806,21 @@ dependencies = [ "serde", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strict-num" version = "0.1.1" @@ -1787,9 +1829,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -1846,15 +1888,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tiny-skia" version = "0.11.4" @@ -1882,15 +1915,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "toml_datetime", @@ -1912,9 +1945,6 @@ name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", -] [[package]] name = "ttf-parser" @@ -1954,11 +1984,25 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.12.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ "getrandom", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "variadics_please" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2047,9 +2091,12 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] [[package]] name = "wasm-bindgen" @@ -2124,12 +2171,12 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", - "downcast-rs", + "downcast-rs 1.2.1", "rustix", "scoped-tls", "smallvec", @@ -2138,11 +2185,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "rustix", "wayland-backend", "wayland-scanner", @@ -2154,16 +2201,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" dependencies = [ "rustix", "wayland-client", @@ -2172,11 +2219,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.6" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -2184,11 +2231,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -2197,11 +2244,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -2251,6 +2298,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wgpu-types" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +dependencies = [ + "bitflags 2.9.1", + "js-sys", + "log", + "serde", + "web-sys", +] + [[package]] name = "winapi-util" version = "0.1.9" @@ -2326,13 +2386,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -2351,6 +2427,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -2369,6 +2451,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -2387,12 +2475,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -2411,6 +2511,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -2429,6 +2535,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -2447,6 +2559,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2466,15 +2584,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winit" -version = "0.30.9" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winit" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d05bd8908e14618c9609471db04007e644fd9cce6529756046cfc577f9155e" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "bytemuck", "calloop", @@ -2519,13 +2643,22 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -2570,7 +2703,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "dlib", "log", "once_cell", @@ -2585,9 +2718,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "xmlparser" @@ -2597,18 +2730,18 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4542e45..d37056f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,8 @@ vulkano-shaders = "0.35" glam = { version = "0.30" } # ECS -bevy_ecs = "0.15" -bevy_app = "0.15" +bevy_ecs = "0.16" +bevy_app = "0.16" # Log and tracing log = "0.4" diff --git a/flake.lock b/flake.lock index 2853bb7..4f4cf7e 100644 --- a/flake.lock +++ b/flake.lock @@ -44,11 +44,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1742546557, - "narHash": "sha256-QyhimDBaDBtMfRc7kyL28vo+HTwXRPq3hz+BgSJDotw=", + "lastModified": 1747312588, + "narHash": "sha256-MmJvj6mlWzeRwKGLcwmZpKaOPZ5nJb/6al5CXqJsgjo=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bfa9810ff7104a17555ab68ebdeafb6705f129b1", + "rev": "b1bebd0fe266bbd1820019612ead889e96a8fa2d", "type": "github" }, "original": { @@ -73,11 +73,11 @@ ] }, "locked": { - "lastModified": 1742524367, - "narHash": "sha256-KzTwk/5ETJavJZYV1DEWdCx05M4duFCxCpRbQSKWpng=", + "lastModified": 1747363019, + "narHash": "sha256-N4dwkRBmpOosa4gfFkFf/LTD8oOcNkAyvZ07JvRDEf0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "70bf752d176b2ce07417e346d85486acea9040ef", + "rev": "0e624f2b1972a34be1a9b35290ed18ea4b419b6f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 4f7cd2e..ec86293 100644 --- a/flake.nix +++ b/flake.nix @@ -31,12 +31,6 @@ cargo = rust; }); - renderdoc = pkgs.renderdoc.overrideAttrs (oldAttrs: { - cmakeFlags = oldAttrs.cmakeFlags ++ [ - (pkgs.lib.cmakeBool "ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND" true) - ]; - }); - buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ stdenv.cc.cc.lib @@ -62,7 +56,7 @@ mkCustomShell = { packages ? [ ] }: pkgs.mkShell { nativeBuildInputs = [ - renderdoc + pkgs.renderdoc (rust.override { extensions = [ "rust-src" "rust-analyzer" ]; }) ] ++ nativeBuildInputs; @@ -70,7 +64,7 @@ ++ packages; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; - VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${renderdoc}/share/vulkan/implicit_layer.d"; + VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; RUST_LOG = "info,rust_vulkan_test=trace"; }; in diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 00822fd..b8889a3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.85.1" +channel = "1.87.0" diff --git a/src/core/vulkan/vulkan_context.rs b/src/core/vulkan/vulkan_context.rs index 74df916..1583df8 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/src/core/vulkan/vulkan_context.rs @@ -1,7 +1,7 @@ use std::{any::Any, sync::Arc}; use bevy_app::App; -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, diff --git a/src/core/vulkan/window_render_context.rs b/src/core/vulkan/window_render_context.rs index 5cb2618..146be52 100644 --- a/src/core/vulkan/window_render_context.rs +++ b/src/core/vulkan/window_render_context.rs @@ -1,5 +1,5 @@ use bevy_app::App; -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use std::sync::Arc; use vulkano::image::view::ImageView; use vulkano::image::{Image, ImageUsage}; diff --git a/src/core/window/config.rs b/src/core/window/config.rs index 8fbef8c..487140c 100644 --- a/src/core/window/config.rs +++ b/src/core/window/config.rs @@ -1,4 +1,4 @@ -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use winit::{dpi::PhysicalSize, window::WindowAttributes}; #[derive(Resource, Clone)] diff --git a/src/core/window/raw_handle.rs b/src/core/window/raw_handle.rs index ad9c8dd..c896b56 100644 --- a/src/core/window/raw_handle.rs +++ b/src/core/window/raw_handle.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use bevy_ecs::system::Resource; +use bevy_ecs::resource::Resource; use winit::{event_loop::EventLoopProxy, window::Window}; #[derive(Resource)] From 6639f0bb1ef04030af41a6ac990bf62445ea6e92 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Fri, 16 May 2025 14:22:18 +0200 Subject: [PATCH 19/28] Init plugins + first system --- src/core/window/mod.rs | 7 ++++++- src/core/window/state.rs | 28 +++++++++++++++++++++++++--- src/game/mod.rs | 5 ++++- src/game/test_plugin.rs | 13 +++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 src/game/test_plugin.rs diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index d1623b7..4a19fee 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,4 +1,4 @@ -use bevy_app::{App, AppExit}; +use bevy_app::{App, AppExit, PluginsState}; use config::WindowConfig; use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; use state::WindowState; @@ -35,6 +35,11 @@ impl Window { } fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { + if app.plugins_state() == PluginsState::Ready { + app.finish(); + app.cleanup(); + } + app.world_mut() .insert_resource(EventLoopProxyWrapper::new(event_loop.create_proxy())); diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 8ffd44b..58d78d2 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,13 +1,16 @@ use std::sync::Arc; -use bevy_app::App; -use bevy_ecs::world::World; +use bevy_app::{App, PluginsState}; +use bevy_ecs::{event, world::World}; use winit::{ application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId, }; -use super::{config::WindowConfig, raw_handle::WindowWrapper}; +use super::{ + config::WindowConfig, + raw_handle::{DisplayHandleWrapper, WindowWrapper}, +}; pub struct WindowState { app: App, @@ -33,13 +36,32 @@ impl ApplicationHandler for WindowState { .insert_resource(WindowWrapper(Arc::new(window))); } + fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) { + if self.app.plugins_state() == PluginsState::Ready { + self.app.finish(); + self.app.cleanup(); + } + } + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { match event { WindowEvent::CloseRequested => { log::debug!("The close button was pressed; stopping"); event_loop.exit(); } + WindowEvent::RedrawRequested => { + log::debug!("The window was requested to be redrawn"); + if self.app.plugins_state() == PluginsState::Cleaned { + self.app.update(); + } + } _ => {} } } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let window_wrapper = self.app.world().get_resource::().unwrap(); + + window_wrapper.0.request_redraw(); + } } diff --git a/src/game/mod.rs b/src/game/mod.rs index 6eb9cab..903a179 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -5,6 +5,8 @@ use crate::core::{ window::{Window, config::WindowConfig}, }; +pub mod test_plugin; + pub fn init(app: &mut App) { let window_config = WindowConfig { title: "Rust ASH Test".to_string(), @@ -12,6 +14,7 @@ pub fn init(app: &mut App) { height: 600, }; + app.add_plugins(test_plugin::TestPlugin); Window::new(app, window_config).unwrap(); - Vulkan::new(app).unwrap(); + // Vulkan::new(app).unwrap(); } diff --git a/src/game/test_plugin.rs b/src/game/test_plugin.rs new file mode 100644 index 0000000..e82e61b --- /dev/null +++ b/src/game/test_plugin.rs @@ -0,0 +1,13 @@ +use bevy_app::{App, Last, Plugin, Startup}; + +pub struct TestPlugin; + +impl Plugin for TestPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Last, setup_system); + } +} + +fn setup_system() { + log::info!("Hello, world!"); +} From 99be029ff879bf16f5782c551b83c574299792fe Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 12:41:25 +0200 Subject: [PATCH 20/28] First vulkan init working --- src/core/vulkan/mod.rs | 18 ++++++++++++------ src/core/window/mod.rs | 17 +++++++++-------- src/core/window/state.rs | 17 +++++++---------- src/game/mod.rs | 12 ++++++++---- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs index 29fe224..b4833ac 100644 --- a/src/core/vulkan/mod.rs +++ b/src/core/vulkan/mod.rs @@ -1,7 +1,9 @@ use vulkan_context::VulkanContext; use window_render_context::WindowRenderContext; -use bevy_app::App; +use bevy_app::{App, Plugin}; + +use super::window::raw_handle::WindowWrapper; mod utils; mod vulkan_context; @@ -13,16 +15,20 @@ pub enum VulkanError { FailedToCreateVulkanContext, } -pub struct Vulkan; +pub struct VulkanPlugin; -impl Vulkan { - pub fn new(app: &mut App) -> Result<(), VulkanError> { +impl Plugin for VulkanPlugin { + fn build(&self, app: &mut App) { let vulkan_context = VulkanContext::from(app as &App); app.world_mut().insert_resource(vulkan_context); + } + fn ready(&self, app: &App) -> bool { + app.world().get_resource::().is_some() + } + + fn finish(&self, app: &mut App) { let window_render_context = WindowRenderContext::from(app as &App); app.world_mut().insert_resource(window_render_context); - - Ok(()) } } diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs index 4a19fee..1576e63 100644 --- a/src/core/window/mod.rs +++ b/src/core/window/mod.rs @@ -1,4 +1,4 @@ -use bevy_app::{App, AppExit, PluginsState}; +use bevy_app::{App, AppExit, Plugin, PluginsState}; use config::WindowConfig; use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; use state::WindowState; @@ -14,23 +14,24 @@ pub enum WindowError { FailedToCreateEventLoop, } -pub struct Window; +pub struct WindowPlugin { + pub window_config: WindowConfig, +} -impl Window { - pub fn new(app: &mut App, window_config: WindowConfig) -> Result<(), WindowError> { +impl Plugin for WindowPlugin { + fn build(&self, app: &mut App) { let world = app.world_mut(); - world.insert_resource(window_config); + world.insert_resource(self.window_config.clone()); let mut event_loop_builder = EventLoop::with_user_event(); let event_loop = event_loop_builder .build() - .map_err(|_| WindowError::FailedToCreateEventLoop)?; + .map_err(|_| WindowError::FailedToCreateEventLoop) + .expect("Failed to create event loop"); world.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle())); app.set_runner(Box::new(move |app| runner(app, event_loop))); - - Ok(()) } } diff --git a/src/core/window/state.rs b/src/core/window/state.rs index 58d78d2..5654022 100644 --- a/src/core/window/state.rs +++ b/src/core/window/state.rs @@ -1,16 +1,13 @@ use std::sync::Arc; use bevy_app::{App, PluginsState}; -use bevy_ecs::{event, world::World}; +use bevy_ecs::world::World; use winit::{ application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId, }; -use super::{ - config::WindowConfig, - raw_handle::{DisplayHandleWrapper, WindowWrapper}, -}; +use super::{config::WindowConfig, raw_handle::WindowWrapper}; pub struct WindowState { app: App, @@ -54,14 +51,14 @@ impl ApplicationHandler for WindowState { if self.app.plugins_state() == PluginsState::Cleaned { self.app.update(); } + + let window_wrapper = self.app.world().get_resource::().unwrap(); + + window_wrapper.0.request_redraw(); } _ => {} } } - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let window_wrapper = self.app.world().get_resource::().unwrap(); - - window_wrapper.0.request_redraw(); - } + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {} } diff --git a/src/game/mod.rs b/src/game/mod.rs index 903a179..e141086 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,8 +1,8 @@ use bevy_app::App; use crate::core::{ - vulkan::Vulkan, - window::{Window, config::WindowConfig}, + vulkan, + window::{self, config::WindowConfig}, }; pub mod test_plugin; @@ -14,7 +14,11 @@ pub fn init(app: &mut App) { height: 600, }; - app.add_plugins(test_plugin::TestPlugin); - Window::new(app, window_config).unwrap(); + app.add_plugins(( + test_plugin::TestPlugin, + window::WindowPlugin { window_config }, + vulkan::VulkanPlugin, + )); + // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); } From b977f446d35e9ce861dd33ed9936fc2fe1e803fd Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 13:15:29 +0200 Subject: [PATCH 21/28] Split crates --- Cargo.lock | 104 +++++------------- Cargo.toml | 22 +++- crates/engine_vulkan/Cargo.toml | 14 +++ .../mod.rs => crates/engine_vulkan/src/lib.rs | 3 +- .../engine_vulkan/src}/utils.rs | 0 .../engine_vulkan/src}/vulkan_context.rs | 3 +- .../src}/window_render_context.rs | 3 +- crates/engine_window/Cargo.toml | 12 ++ .../engine_window/src}/config.rs | 0 .../mod.rs => crates/engine_window/src/lib.rs | 0 .../engine_window/src}/raw_handle.rs | 0 .../engine_window/src}/state.rs | 1 - flake.nix | 2 +- src/core/mod.rs | 2 - src/game/mod.rs | 15 +-- src/game/test_plugin.rs | 13 --- 16 files changed, 84 insertions(+), 110 deletions(-) create mode 100644 crates/engine_vulkan/Cargo.toml rename src/core/vulkan/mod.rs => crates/engine_vulkan/src/lib.rs (94%) rename {src/core/vulkan => crates/engine_vulkan/src}/utils.rs (100%) rename {src/core/vulkan => crates/engine_vulkan/src}/vulkan_context.rs (97%) rename {src/core/vulkan => crates/engine_vulkan/src}/window_render_context.rs (98%) create mode 100644 crates/engine_window/Cargo.toml rename {src/core/window => crates/engine_window/src}/config.rs (100%) rename src/core/window/mod.rs => crates/engine_window/src/lib.rs (100%) rename {src/core/window => crates/engine_window/src}/raw_handle.rs (100%) rename {src/core/window => crates/engine_window/src}/state.rs (96%) delete mode 100644 src/game/test_plugin.rs diff --git a/Cargo.lock b/Cargo.lock index e9d2554..d2dda1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,12 +117,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - [[package]] name = "arrayref" version = "0.3.9" @@ -676,6 +670,32 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "engine_vulkan" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", + "engine_window", + "env_logger", + "log", + "thiserror 2.0.12", + "vulkano", + "winit", +] + +[[package]] +name = "engine_window" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", + "env_logger", + "log", + "thiserror 2.0.12", + "winit", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -986,7 +1006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.52.6", ] [[package]] @@ -1621,13 +1641,13 @@ dependencies = [ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ - "anyhow", "bevy_app", "bevy_ecs", + "engine_vulkan", + "engine_window", "env_logger", "glam 0.30.3", "log", - "thiserror 2.0.12", "vulkano", "vulkano-shaders", "winit", @@ -2386,29 +2406,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -2427,12 +2431,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -2451,12 +2449,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -2475,24 +2467,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -2511,12 +2491,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -2535,12 +2509,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -2559,12 +2527,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2583,12 +2545,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winit" version = "0.30.10" diff --git a/Cargo.toml b/Cargo.toml index d37056f..c5f1546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,11 @@ edition = "2024" authors = ["Florian RICHER "] publish = false -[dependencies] +[workspace] +resolver = "2" +members = ["crates/*"] + +[workspace.dependencies] anyhow = "1.0" thiserror = "2.0" winit = { version = "0.30", features = ["rwh_06"] } @@ -23,3 +27,19 @@ bevy_app = "0.16" # Log and tracing log = "0.4" env_logger = "0.11" + +engine_vulkan = { path = "crates/engine_vulkan" } +engine_window = { path = "crates/engine_window" } + +[dependencies] +log = { workspace = true } +env_logger = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } +winit = { workspace = true } +vulkano = { workspace = true } +vulkano-shaders = { workspace = true } +glam = { workspace = true } + +engine_vulkan = { workspace = true } +engine_window = { workspace = true } diff --git a/crates/engine_vulkan/Cargo.toml b/crates/engine_vulkan/Cargo.toml new file mode 100644 index 0000000..c0fe35b --- /dev/null +++ b/crates/engine_vulkan/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "engine_vulkan" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } +winit = { workspace = true } +vulkano = { workspace = true } +engine_window = { workspace = true } diff --git a/src/core/vulkan/mod.rs b/crates/engine_vulkan/src/lib.rs similarity index 94% rename from src/core/vulkan/mod.rs rename to crates/engine_vulkan/src/lib.rs index b4833ac..e36006e 100644 --- a/src/core/vulkan/mod.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,10 +1,9 @@ +use engine_window::raw_handle::WindowWrapper; use vulkan_context::VulkanContext; use window_render_context::WindowRenderContext; use bevy_app::{App, Plugin}; -use super::window::raw_handle::WindowWrapper; - mod utils; mod vulkan_context; mod window_render_context; diff --git a/src/core/vulkan/utils.rs b/crates/engine_vulkan/src/utils.rs similarity index 100% rename from src/core/vulkan/utils.rs rename to crates/engine_vulkan/src/utils.rs diff --git a/src/core/vulkan/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs similarity index 97% rename from src/core/vulkan/vulkan_context.rs rename to crates/engine_vulkan/src/vulkan_context.rs index 1583df8..7bf3b3b 100644 --- a/src/core/vulkan/vulkan_context.rs +++ b/crates/engine_vulkan/src/vulkan_context.rs @@ -2,6 +2,7 @@ use std::{any::Any, sync::Arc}; use bevy_app::App; use bevy_ecs::resource::Resource; +use engine_window::raw_handle::DisplayHandleWrapper; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, @@ -15,8 +16,6 @@ use vulkano::{ }; use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use crate::core::window::raw_handle::DisplayHandleWrapper; - use super::utils; #[derive(Resource)] diff --git a/src/core/vulkan/window_render_context.rs b/crates/engine_vulkan/src/window_render_context.rs similarity index 98% rename from src/core/vulkan/window_render_context.rs rename to crates/engine_vulkan/src/window_render_context.rs index 146be52..87ee958 100644 --- a/src/core/vulkan/window_render_context.rs +++ b/crates/engine_vulkan/src/window_render_context.rs @@ -1,5 +1,6 @@ use bevy_app::App; use bevy_ecs::resource::Resource; +use engine_window::raw_handle::WindowWrapper; use std::sync::Arc; use vulkano::image::view::ImageView; use vulkano::image::{Image, ImageUsage}; @@ -9,8 +10,6 @@ use vulkano::sync::{self, GpuFuture}; use vulkano::{Validated, VulkanError}; use winit::window::Window; -use crate::core::window::raw_handle::WindowWrapper; - use super::vulkan_context::VulkanContext; #[derive(Resource)] diff --git a/crates/engine_window/Cargo.toml b/crates/engine_window/Cargo.toml new file mode 100644 index 0000000..dc19716 --- /dev/null +++ b/crates/engine_window/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "engine_window" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } +winit = { workspace = true } diff --git a/src/core/window/config.rs b/crates/engine_window/src/config.rs similarity index 100% rename from src/core/window/config.rs rename to crates/engine_window/src/config.rs diff --git a/src/core/window/mod.rs b/crates/engine_window/src/lib.rs similarity index 100% rename from src/core/window/mod.rs rename to crates/engine_window/src/lib.rs diff --git a/src/core/window/raw_handle.rs b/crates/engine_window/src/raw_handle.rs similarity index 100% rename from src/core/window/raw_handle.rs rename to crates/engine_window/src/raw_handle.rs diff --git a/src/core/window/state.rs b/crates/engine_window/src/state.rs similarity index 96% rename from src/core/window/state.rs rename to crates/engine_window/src/state.rs index 5654022..0f708fd 100644 --- a/src/core/window/state.rs +++ b/crates/engine_window/src/state.rs @@ -47,7 +47,6 @@ impl ApplicationHandler for WindowState { event_loop.exit(); } WindowEvent::RedrawRequested => { - log::debug!("The window was requested to be redrawn"); if self.app.plugins_state() == PluginsState::Cleaned { self.app.update(); } diff --git a/flake.nix b/flake.nix index ec86293..b00f3ab 100644 --- a/flake.nix +++ b/flake.nix @@ -65,7 +65,7 @@ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; - RUST_LOG = "info,rust_vulkan_test=trace"; + RUST_LOG = "trace,rust_vulkan_test=trace"; }; in { diff --git a/src/core/mod.rs b/src/core/mod.rs index db2061f..a8707c6 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,2 @@ pub mod camera; pub mod render; -pub mod vulkan; -pub mod window; diff --git a/src/game/mod.rs b/src/game/mod.rs index e141086..2078652 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,11 +1,6 @@ use bevy_app::App; - -use crate::core::{ - vulkan, - window::{self, config::WindowConfig}, -}; - -pub mod test_plugin; +use engine_vulkan::VulkanPlugin; +use engine_window::{WindowPlugin, config::WindowConfig}; pub fn init(app: &mut App) { let window_config = WindowConfig { @@ -14,11 +9,7 @@ pub fn init(app: &mut App) { height: 600, }; - app.add_plugins(( - test_plugin::TestPlugin, - window::WindowPlugin { window_config }, - vulkan::VulkanPlugin, - )); + app.add_plugins((WindowPlugin { window_config }, VulkanPlugin)); // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); } diff --git a/src/game/test_plugin.rs b/src/game/test_plugin.rs deleted file mode 100644 index e82e61b..0000000 --- a/src/game/test_plugin.rs +++ /dev/null @@ -1,13 +0,0 @@ -use bevy_app::{App, Last, Plugin, Startup}; - -pub struct TestPlugin; - -impl Plugin for TestPlugin { - fn build(&self, app: &mut App) { - app.add_systems(Last, setup_system); - } -} - -fn setup_system() { - log::info!("Hello, world!"); -} From f585ba78e73ebcd3e64c28a167c61f4320312044 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 17:06:59 +0200 Subject: [PATCH 22/28] Split vulkan resources --- crates/engine_vulkan/src/lib.rs | 71 ++++- crates/engine_vulkan/src/utils.rs | 137 -------- crates/engine_vulkan/src/utils/device.rs | 301 ++++++++++++++++++ crates/engine_vulkan/src/utils/instance.rs | 70 ++++ crates/engine_vulkan/src/utils/mod.rs | 2 + crates/engine_vulkan/src/vulkan_context.rs | 88 ----- .../src/window_render_context.rs | 31 +- flake.nix | 2 +- src/game/mod.rs | 25 +- 9 files changed, 483 insertions(+), 244 deletions(-) delete mode 100644 crates/engine_vulkan/src/utils.rs create mode 100644 crates/engine_vulkan/src/utils/device.rs create mode 100644 crates/engine_vulkan/src/utils/instance.rs create mode 100644 crates/engine_vulkan/src/utils/mod.rs delete mode 100644 crates/engine_vulkan/src/vulkan_context.rs diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index e36006e..310a1b9 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,25 +1,86 @@ +use std::sync::Arc; + +use bevy_ecs::resource::Resource; use engine_window::raw_handle::WindowWrapper; -use vulkan_context::VulkanContext; +use utils::{device::create_and_insert_device, instance::create_and_insert_instance}; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, DeviceExtensions, DeviceFeatures, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, +}; use window_render_context::WindowRenderContext; use bevy_app::{App, Plugin}; mod utils; -mod vulkan_context; mod window_render_context; +#[derive(Resource)] +pub struct VulkanInstance(Arc); + +#[derive(Resource)] +pub struct VulkanDevice(Arc); + +#[derive(Resource)] +pub struct VulkanGraphicsQueue(Arc); + +#[derive(Resource)] +pub struct VulkanComputeQueue(Arc); + +#[derive(Resource)] +pub struct VulkanTransferQueue(Arc); +#[derive(Resource)] +pub struct VulkanMemoryAllocator(Arc); + +#[derive(Resource)] +pub struct VulkanCommandBufferAllocator(Arc); + +#[derive(Resource)] +pub struct VulkanDescriptorSetAllocator(Arc); + #[derive(Debug, thiserror::Error)] pub enum VulkanError { #[error("Failed to create vulkan context")] FailedToCreateVulkanContext, } -pub struct VulkanPlugin; +pub struct VulkanConfig { + pub instance_layers: Vec, + pub device_extensions: DeviceExtensions, + pub device_features: DeviceFeatures, + pub with_window_surface: bool, + pub with_graphics_queue: bool, + pub with_compute_queue: bool, + pub with_transfer_queue: bool, +} + +impl Default for VulkanConfig { + fn default() -> Self { + Self { + instance_layers: Vec::default(), + device_extensions: DeviceExtensions::default(), + device_features: DeviceFeatures::default(), + with_window_surface: true, + with_graphics_queue: true, + with_compute_queue: true, + with_transfer_queue: true, + } + } +} + +#[derive(Default)] +pub struct VulkanPlugin { + pub vulkan_config: VulkanConfig, +} impl Plugin for VulkanPlugin { fn build(&self, app: &mut App) { - let vulkan_context = VulkanContext::from(app as &App); - app.world_mut().insert_resource(vulkan_context); + let world = app.world_mut(); + + create_and_insert_instance(world, &self.vulkan_config); + create_and_insert_device(world, &self.vulkan_config); } fn ready(&self, app: &App) -> bool { diff --git a/crates/engine_vulkan/src/utils.rs b/crates/engine_vulkan/src/utils.rs deleted file mode 100644 index 046528f..0000000 --- a/crates/engine_vulkan/src/utils.rs +++ /dev/null @@ -1,137 +0,0 @@ -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/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/utils/device.rs new file mode 100644 index 0000000..6c4b49f --- /dev/null +++ b/crates/engine_vulkan/src/utils/device.rs @@ -0,0 +1,301 @@ +use std::sync::Arc; + +use bevy_ecs::world::World; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, + }, + memory::allocator::StandardMemoryAllocator, +}; + +use crate::{ + VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanConfig, VulkanDescriptorSetAllocator, + VulkanDevice, VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator, VulkanTransferQueue, +}; + +pub fn create_and_insert_device(world: &mut World, config: &VulkanConfig) { + let picked_device = + pick_physical_device(world, &config).expect("Failed to pick physical device"); + + let device = picked_device.device; + let physical_device = device.physical_device(); + + log::debug!("Vulkan device created"); + log::debug!( + "\tPhysical device: {:?} ({:?})", + physical_device.properties().device_name, + physical_device.properties().device_type + ); + log::debug!("\tDevice extensions: {:?}", device.enabled_extensions()); + log::debug!("\tDevice features: {:?}", device.enabled_features()); + + world.insert_resource(VulkanDevice(device.clone())); + + log::debug!("\tDevice selected queues:"); + if config.with_graphics_queue { + world.insert_resource(VulkanGraphicsQueue( + picked_device + .graphics_queue + .expect("Failed to get graphics queue"), + )); + log::debug!("\t\t- Graphics queue"); + } + + if config.with_compute_queue { + world.insert_resource(VulkanComputeQueue( + picked_device + .compute_queue + .expect("Failed to get compute queue"), + )); + log::debug!("\t\t- Compute queue"); + } + + if config.with_transfer_queue { + world.insert_resource(VulkanTransferQueue( + picked_device + .transfer_queue + .expect("Failed to get transfer queue"), + )); + log::debug!("\t\t- Transfer queue"); + } + + world.insert_resource(VulkanMemoryAllocator(Arc::new( + StandardMemoryAllocator::new_default(device.clone()), + ))); + world.insert_resource(VulkanCommandBufferAllocator(Arc::new( + StandardCommandBufferAllocator::new(device.clone(), Default::default()), + ))); + world.insert_resource(VulkanDescriptorSetAllocator(Arc::new( + StandardDescriptorSetAllocator::new(device.clone(), Default::default()), + ))); +} + +struct PickedDevice { + pub device: Arc, + pub graphics_queue: Option>, + pub compute_queue: Option>, + pub transfer_queue: Option>, +} + +fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option { + let instance = world + .get_resource::() + .expect("Failed to get VulkanInstance during vulkan plugin initialization"); + + instance + .0 + .enumerate_physical_devices() + .expect("Failed to enumerate physical devices") + .filter_map(|p| check_physical_device(world, &p, config)) + .min_by_key( + |p| match p.device.physical_device().properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }, + ) + .take() +} + +fn check_device_extensions_support( + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + let device_extensions = DeviceExtensions { + khr_swapchain: config.with_window_surface, + ..config.device_extensions + }; + + if physical_device + .supported_extensions() + .contains(&device_extensions) + { + log::debug!( + "\t\t[OK] Device supports required extensions {:?}", + device_extensions + ); + Some(device_extensions) + } else { + log::debug!( + "\t\t[FAILED] Device does not support required extensions {:?}", + device_extensions + ); + None + } +} + +struct PickedQueuesInfo { + graphics_queue_family_index: Option, + compute_queue_family_index: Option, + transfer_queue_family_index: Option, +} + +fn check_queues_support( + world: &World, + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + let mut graphics_queue_family_index: Option = None; + let mut compute_queue_family_index: Option = None; + let mut transfer_queue_family_index: Option = None; + + for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { + if config.with_graphics_queue { + let graphics_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::GRAPHICS); + + let presentation_valid = if config.with_window_surface { + let display_handle = world + .get_resource::() + .expect("DisplayHandleWrapper must be added before VulkanPlugin"); + + physical_device + .presentation_support(i as u32, &display_handle.0) + .expect("Failed to check presentation support") + } else { + true + }; + + if graphics_supported && presentation_valid { + graphics_queue_family_index = Some(i as u32); + } + } + + if config.with_compute_queue { + let compute_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::COMPUTE); + + if compute_supported { + compute_queue_family_index = Some(i as u32); + } + } + + if config.with_transfer_queue { + let transfer_supported = queue_family_property + .queue_flags + .intersects(QueueFlags::TRANSFER); + + if transfer_supported { + transfer_queue_family_index = Some(i as u32); + } + } + } + + if !config.with_graphics_queue { + log::debug!("\t\t[SKIPPED] Graphics queue is not required"); + } else if graphics_queue_family_index.is_some() { + log::debug!("\t\t[OK] Graphics queue is supported"); + } else { + log::debug!("\t\t[FAILED] Graphics queue is not supported"); + return None; + } + + if !config.with_compute_queue { + log::debug!("\t\t[SKIPPED] Compute queue is not required"); + } else if compute_queue_family_index.is_some() { + log::debug!("\t\t[OK] Compute queue is supported"); + } else { + log::debug!("\t\t[FAILED] Compute queue is not supported"); + return None; + } + + if !config.with_transfer_queue { + log::debug!("\t\t[SKIPPED] Transfer queue is not required"); + } else if transfer_queue_family_index.is_some() { + log::debug!("\t\t[OK] Transfer queue is supported"); + } else { + log::debug!("\t\t[FAILED] Transfer queue is not supported"); + return None; + } + + Some(PickedQueuesInfo { + graphics_queue_family_index, + compute_queue_family_index, + transfer_queue_family_index, + }) +} + +fn check_physical_device( + world: &World, + physical_device: &Arc, + config: &VulkanConfig, +) -> Option { + log::debug!("Checking physical device"); + log::debug!("\tProperties"); + log::debug!("\t\tName: {}", physical_device.properties().device_name); + log::debug!("\t\tAPI version: {}", physical_device.api_version()); + log::debug!( + "\t\tDevice type: {:?}", + physical_device.properties().device_type + ); + log::debug!("\tRequired supports checking report"); + + let device_extensions = check_device_extensions_support(physical_device, config)?; + let picked_queues_info = check_queues_support(world, physical_device, config)?; + + let mut queue_create_infos = vec![]; + + if config.with_graphics_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), + ..Default::default() + }); + } + + if config.with_compute_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), + ..Default::default() + }); + } + + if config.with_transfer_queue { + queue_create_infos.push(QueueCreateInfo { + queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), + ..Default::default() + }); + } + + let (device, mut queues) = Device::new( + physical_device.clone(), + DeviceCreateInfo { + queue_create_infos, + enabled_extensions: device_extensions, + enabled_features: config.device_features, + ..Default::default() + }, + ) + .expect("Failed to create device"); + + let mut graphics_queue = None; + let mut compute_queue = None; + let mut transfer_queue = None; + + if config.with_graphics_queue { + graphics_queue = queues.next(); + } + + if config.with_compute_queue { + compute_queue = queues.next(); + } + + if config.with_transfer_queue { + transfer_queue = queues.next(); + } + + Some(PickedDevice { + device, + graphics_queue, + compute_queue, + transfer_queue, + }) +} diff --git a/crates/engine_vulkan/src/utils/instance.rs b/crates/engine_vulkan/src/utils/instance.rs new file mode 100644 index 0000000..dcda857 --- /dev/null +++ b/crates/engine_vulkan/src/utils/instance.rs @@ -0,0 +1,70 @@ +use std::sync::Arc; + +use bevy_ecs::world::World; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + VulkanLibrary, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + swapchain::Surface, +}; + +use crate::{VulkanConfig, VulkanInstance}; + +fn load_library() -> Arc { + let library = VulkanLibrary::new().unwrap(); + + log::debug!("Available Instance layers:"); + 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 fn create_and_insert_instance(world: &mut World, config: &VulkanConfig) { + let library = load_library(); + + let instance_extensions = { + if config.with_window_surface { + let display_handle = world + .get_resource::() + .expect("DisplayHandleWrapper must be added before VulkanPlugin"); + + Surface::required_extensions(&display_handle.0) + .expect("Failed to get surface required extensions") + } else { + InstanceExtensions::default() + } + }; + + let instance = Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: instance_extensions, + enabled_layers: config.instance_layers.clone(), + ..Default::default() + }, + ) + .expect("Failed to create vulkan instance"); + + log::debug!("Instance created"); + log::debug!( + "\t- Enabled extensions: {:?}", + instance.enabled_extensions() + ); + log::debug!("\t- Enabled layers: {:?}", instance.enabled_layers()); + log::debug!("\t- API version: {:?}", instance.api_version()); + log::debug!("\t- Max API version: {:?}", instance.max_api_version()); + log::debug!("\t- Flags: {:?}", instance.flags()); + + world.insert_resource(VulkanInstance(instance)); +} diff --git a/crates/engine_vulkan/src/utils/mod.rs b/crates/engine_vulkan/src/utils/mod.rs new file mode 100644 index 0000000..09e4b35 --- /dev/null +++ b/crates/engine_vulkan/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod device; +pub mod instance; diff --git a/crates/engine_vulkan/src/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs deleted file mode 100644 index 7bf3b3b..0000000 --- a/crates/engine_vulkan/src/vulkan_context.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::{any::Any, sync::Arc}; - -use bevy_app::App; -use bevy_ecs::resource::Resource; -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::{ - 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 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 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(); - - let world = app.world(); - - let display_handle: &DisplayHandleWrapper = - world.get_resource::().unwrap(); - - let enabled_extensions = Surface::required_extensions(&display_handle.0).unwrap(); - log::debug!("Surface required extensions: {enabled_extensions:?}"); - - 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/crates/engine_vulkan/src/window_render_context.rs b/crates/engine_vulkan/src/window_render_context.rs index 87ee958..95b3d90 100644 --- a/crates/engine_vulkan/src/window_render_context.rs +++ b/crates/engine_vulkan/src/window_render_context.rs @@ -5,12 +5,12 @@ 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::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use vulkano::sync::{self, GpuFuture}; use vulkano::{Validated, VulkanError}; use winit::window::Window; -use super::vulkan_context::VulkanContext; +use crate::{VulkanDevice, VulkanInstance}; #[derive(Resource)] pub struct WindowRenderContext { @@ -25,27 +25,36 @@ pub struct WindowRenderContext { 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_handle = world + .get_resource::() + .expect("Failed to find window handle"); + let vulkan_instance = world + .get_resource::() + .expect("Failed to find vulkan instance"); + let vulkan_device = world + .get_resource::() + .expect("Failed to find vulkan device"); + let window_size = window_handle.0.inner_size(); - let surface = vulkan_context.create_surface(window_handle.0.clone()); + let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone()) + .expect("Failed to create surface"); let (swapchain, images) = { - let surface_capabilities = vulkan_context - .device + let surface_capabilities = vulkan_device + .0 .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let (image_format, _) = vulkan_context - .device + let (image_format, _) = vulkan_device + .0 .physical_device() .surface_formats(&surface, Default::default()) .unwrap()[0]; Swapchain::new( - vulkan_context.device.clone(), + vulkan_device.0.clone(), surface, SwapchainCreateInfo { // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. @@ -74,7 +83,7 @@ impl From<&App> for WindowRenderContext { }; let recreate_swapchain = false; - let previous_frame_end = Some(sync::now(vulkan_context.device.clone()).boxed_send_sync()); + let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync()); Self { window: window_handle.0.clone(), diff --git a/flake.nix b/flake.nix index b00f3ab..9cef9c1 100644 --- a/flake.nix +++ b/flake.nix @@ -65,7 +65,7 @@ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d"; - RUST_LOG = "trace,rust_vulkan_test=trace"; + RUST_LOG = "debug,rust_vulkan_test=trace"; }; in { diff --git a/src/game/mod.rs b/src/game/mod.rs index 2078652..2c30bf8 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,6 +1,7 @@ use bevy_app::App; -use engine_vulkan::VulkanPlugin; +use engine_vulkan::{VulkanConfig, VulkanPlugin}; use engine_window::{WindowPlugin, config::WindowConfig}; +use vulkano::device::{DeviceExtensions, DeviceFeatures}; pub fn init(app: &mut App) { let window_config = WindowConfig { @@ -9,7 +10,27 @@ pub fn init(app: &mut App) { height: 600, }; - app.add_plugins((WindowPlugin { window_config }, VulkanPlugin)); + let device_extensions = DeviceExtensions { + khr_dynamic_rendering: true, + ..Default::default() + }; + + let device_features = DeviceFeatures { + dynamic_rendering: true, + ..Default::default() + }; + + let vulkan_config = VulkanConfig { + instance_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + device_extensions, + device_features, + ..Default::default() + }; + + app.add_plugins(( + WindowPlugin { window_config }, + VulkanPlugin { vulkan_config }, + )); // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); } From 0ee29a3649c507f451ae7f63e06deacc67664c91 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 18:02:54 +0200 Subject: [PATCH 23/28] render_plugin: Add first SubApp and default schedules --- Cargo.lock | 11 +++- Cargo.toml | 2 + crates/engine_render/Cargo.toml | 9 +++ crates/engine_render/src/lib.rs | 65 ++++++++++++++++++ crates/engine_vulkan/src/vulkan_context.rs | 76 ++++++++++++++++++++++ crates/engine_window/Cargo.toml | 1 - src/game/mod.rs | 2 + 7 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 crates/engine_render/Cargo.toml create mode 100644 crates/engine_render/src/lib.rs create mode 100644 crates/engine_vulkan/src/vulkan_context.rs diff --git a/Cargo.lock b/Cargo.lock index d2dda1a..6467d38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -670,6 +670,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "engine_render" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", + "log", +] + [[package]] name = "engine_vulkan" version = "0.1.0" @@ -690,7 +699,6 @@ version = "0.1.0" dependencies = [ "bevy_app", "bevy_ecs", - "env_logger", "log", "thiserror 2.0.12", "winit", @@ -1643,6 +1651,7 @@ version = "0.1.0" dependencies = [ "bevy_app", "bevy_ecs", + "engine_render", "engine_vulkan", "engine_window", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index c5f1546..d8c8792 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ env_logger = "0.11" engine_vulkan = { path = "crates/engine_vulkan" } engine_window = { path = "crates/engine_window" } +engine_render = { path = "crates/engine_render" } [dependencies] log = { workspace = true } @@ -43,3 +44,4 @@ glam = { workspace = true } engine_vulkan = { workspace = true } engine_window = { workspace = true } +engine_render = { workspace = true } diff --git a/crates/engine_render/Cargo.toml b/crates/engine_render/Cargo.toml new file mode 100644 index 0000000..bd283f0 --- /dev/null +++ b/crates/engine_render/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "engine_render" +version = "0.1.0" +edition = "2024" + +[dependencies] +log = { workspace = true } +bevy_app = { workspace = true } +bevy_ecs = { workspace = true } diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs new file mode 100644 index 0000000..febefbf --- /dev/null +++ b/crates/engine_render/src/lib.rs @@ -0,0 +1,65 @@ +use bevy_app::{App, AppLabel, Last, Plugin, SubApp}; +use bevy_ecs::{ + schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet}, + system::Commands, +}; + +#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] +pub enum RenderSystems { + Prepare, + Queue, + Render, + Present, +} + +#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)] +pub struct Render; + +impl Render { + pub fn base_schedule() -> Schedule { + use RenderSystems::*; + + let mut schedule = Schedule::new(Self); + + schedule.configure_sets((Prepare, Queue, Render, Present).chain()); + + schedule + } +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)] +pub struct RenderApp; + +pub struct RenderPlugin; + +impl Plugin for RenderPlugin { + fn build(&self, app: &mut App) { + 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, test_prepare.in_set(RenderSystems::Prepare)); + render_app.add_systems(Render, test_queue.in_set(RenderSystems::Queue)); + render_app.add_systems(Render, test_render.in_set(RenderSystems::Render)); + render_app.add_systems(Render, test_present.in_set(RenderSystems::Present)); + + app.insert_sub_app(RenderApp, render_app); + } +} + +fn test_prepare(mut commands: Commands) { + log::info!("test_prepare"); +} + +fn test_queue(mut commands: Commands) { + log::info!("test_queue"); +} + +fn test_render(mut commands: Commands) { + log::info!("test_render"); +} + +fn test_present(mut commands: Commands) { + log::info!("test_present"); +} diff --git a/crates/engine_vulkan/src/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs new file mode 100644 index 0000000..08ff1ac --- /dev/null +++ b/crates/engine_vulkan/src/vulkan_context.rs @@ -0,0 +1,76 @@ +use std::{any::Any, sync::Arc}; + +use bevy_app::App; +use bevy_ecs::resource::Resource; +use engine_window::raw_handle::DisplayHandleWrapper; +use vulkano::{ + 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 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 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 (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/crates/engine_window/Cargo.toml b/crates/engine_window/Cargo.toml index dc19716..b5016cc 100644 --- a/crates/engine_window/Cargo.toml +++ b/crates/engine_window/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] thiserror = { workspace = true } log = { workspace = true } -env_logger = { workspace = true } bevy_app = { workspace = true } bevy_ecs = { workspace = true } winit = { workspace = true } diff --git a/src/game/mod.rs b/src/game/mod.rs index 2c30bf8..ff3df3c 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,4 +1,5 @@ use bevy_app::App; +use engine_render::RenderPlugin; use engine_vulkan::{VulkanConfig, VulkanPlugin}; use engine_window::{WindowPlugin, config::WindowConfig}; use vulkano::device::{DeviceExtensions, DeviceFeatures}; @@ -30,6 +31,7 @@ pub fn init(app: &mut App) { app.add_plugins(( WindowPlugin { window_config }, VulkanPlugin { vulkan_config }, + RenderPlugin, )); // Window::new(app, window_config).unwrap(); // Vulkan::new(app).unwrap(); From ae0a2be097a0565cb862d7384a292ffec0958227 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 19:28:34 +0200 Subject: [PATCH 24/28] render_plugin: Begin add window plugin --- Cargo.lock | 3 + crates/engine_render/Cargo.toml | 3 + crates/engine_render/src/lib.rs | 82 ++++++++++++++++++++------ crates/engine_render/src/window/mod.rs | 35 +++++++++++ crates/engine_vulkan/src/lib.rs | 42 ++++++------- crates/engine_window/src/lib.rs | 2 +- crates/engine_window/src/raw_handle.rs | 4 +- src/game/mod.rs | 11 +++- 8 files changed, 134 insertions(+), 48 deletions(-) create mode 100644 crates/engine_render/src/window/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6467d38..e538714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -676,7 +676,10 @@ version = "0.1.0" dependencies = [ "bevy_app", "bevy_ecs", + "engine_vulkan", + "engine_window", "log", + "vulkano", ] [[package]] diff --git a/crates/engine_render/Cargo.toml b/crates/engine_render/Cargo.toml index bd283f0..5016279 100644 --- a/crates/engine_render/Cargo.toml +++ b/crates/engine_render/Cargo.toml @@ -7,3 +7,6 @@ edition = "2024" log = { workspace = true } bevy_app = { workspace = true } bevy_ecs = { workspace = true } +vulkano = { workspace = true } +engine_vulkan = { workspace = true } +engine_window = { workspace = true } diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs index febefbf..f42013e 100644 --- a/crates/engine_render/src/lib.rs +++ b/crates/engine_render/src/lib.rs @@ -1,11 +1,21 @@ use bevy_app::{App, AppLabel, Last, Plugin, SubApp}; use bevy_ecs::{ schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet}, - system::Commands, + system::{Commands, Res}, + world::World, }; +use engine_vulkan::{ + VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, + VulkanMemoryAllocator, +}; +use engine_window::raw_handle::WindowWrapper; +use window::WindowRenderPlugin; + +pub mod window; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum RenderSystems { + ManageViews, Prepare, Queue, Render, @@ -33,33 +43,69 @@ pub struct RenderApp; pub struct RenderPlugin; impl Plugin for RenderPlugin { - fn build(&self, app: &mut App) { + fn build(&self, _app: &mut App) {} + + fn ready(&self, app: &App) -> bool { + let world = app.world(); + + world.get_resource::().is_some() + && world.get_resource::().is_some() + && world.get_resource::().is_some() + && world.get_resource::().is_some() + && world + .get_resource::() + .is_some() + && world + .get_resource::() + .is_some() + } + + fn finish(&self, app: &mut App) { 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, test_prepare.in_set(RenderSystems::Prepare)); - render_app.add_systems(Render, test_queue.in_set(RenderSystems::Queue)); - render_app.add_systems(Render, test_render.in_set(RenderSystems::Render)); - render_app.add_systems(Render, test_present.in_set(RenderSystems::Present)); + extract_app_resources(app.world_mut(), render_app.world_mut()); app.insert_sub_app(RenderApp, render_app); + + app.add_plugins(WindowRenderPlugin); + } + + fn cleanup(&self, app: &mut App) { + app.remove_sub_app(RenderApp); } } -fn test_prepare(mut commands: Commands) { - log::info!("test_prepare"); -} +fn extract_app_resources(world: &mut World, render_world: &mut World) { + let window_wrapper = world + .get_resource::() + .expect("Failed to get WindowWrapper. Check is WindowPlugin is added before RenderPlugin."); -fn test_queue(mut commands: Commands) { - log::info!("test_queue"); -} + let vulkan_device = world + .get_resource::() + .expect("Failed to get Vulkan device. Check is VulkanPlugin is added before RenderPlugin."); -fn test_render(mut commands: Commands) { - log::info!("test_render"); -} + let vulkan_graphics_queue = world.get_resource::().expect( + "Failed to get Vulkan graphics queue. Check is VulkanPlugin is added before RenderPlugin.", + ); -fn test_present(mut commands: Commands) { - log::info!("test_present"); + let vulkan_memory_allocator = world + .get_resource::() + .expect("Failed to get Vulkan memory allocator. Check is VulkanPlugin is added before RenderPlugin."); + + let vulkan_command_buffer_allocator = world + .get_resource::() + .expect("Failed to get Vulkan command buffer allocator. Check is VulkanPlugin is added before RenderPlugin."); + + let vulkan_descriptor_set_allocator = world + .get_resource::() + .expect("Failed to get Vulkan descriptor set allocator. Check is VulkanPlugin is added before RenderPlugin."); + + render_world.insert_resource(vulkan_device.clone()); + render_world.insert_resource(vulkan_graphics_queue.clone()); + render_world.insert_resource(vulkan_memory_allocator.clone()); + render_world.insert_resource(vulkan_command_buffer_allocator.clone()); + render_world.insert_resource(vulkan_descriptor_set_allocator.clone()); + render_world.insert_resource(window_wrapper.clone()); } diff --git a/crates/engine_render/src/window/mod.rs b/crates/engine_render/src/window/mod.rs new file mode 100644 index 0000000..bcb3ea6 --- /dev/null +++ b/crates/engine_render/src/window/mod.rs @@ -0,0 +1,35 @@ +use std::sync::Arc; + +use bevy_app::{App, Plugin}; +use bevy_ecs::resource::Resource; +use vulkano::{ + image::view::ImageView, pipeline::graphics::viewport::Viewport, swapchain::Swapchain, + sync::GpuFuture, +}; + +use crate::RenderApp; + +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::(); + } +} diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index 310a1b9..65e9349 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -17,28 +17,29 @@ use bevy_app::{App, Plugin}; mod utils; mod window_render_context; -#[derive(Resource)] -pub struct VulkanInstance(Arc); +#[derive(Resource, Clone)] +pub struct VulkanInstance(pub Arc); -#[derive(Resource)] -pub struct VulkanDevice(Arc); +#[derive(Resource, Clone)] +pub struct VulkanDevice(pub Arc); -#[derive(Resource)] -pub struct VulkanGraphicsQueue(Arc); +#[derive(Resource, Clone)] +pub struct VulkanGraphicsQueue(pub Arc); -#[derive(Resource)] -pub struct VulkanComputeQueue(Arc); +#[derive(Resource, Clone)] +pub struct VulkanComputeQueue(pub Arc); -#[derive(Resource)] -pub struct VulkanTransferQueue(Arc); -#[derive(Resource)] -pub struct VulkanMemoryAllocator(Arc); +#[derive(Resource, Clone)] +pub struct VulkanTransferQueue(pub Arc); -#[derive(Resource)] -pub struct VulkanCommandBufferAllocator(Arc); +#[derive(Resource, Clone)] +pub struct VulkanMemoryAllocator(pub Arc); -#[derive(Resource)] -pub struct VulkanDescriptorSetAllocator(Arc); +#[derive(Resource, Clone)] +pub struct VulkanCommandBufferAllocator(pub Arc); + +#[derive(Resource, Clone)] +pub struct VulkanDescriptorSetAllocator(pub Arc); #[derive(Debug, thiserror::Error)] pub enum VulkanError { @@ -82,13 +83,4 @@ impl Plugin for VulkanPlugin { create_and_insert_instance(world, &self.vulkan_config); create_and_insert_device(world, &self.vulkan_config); } - - fn ready(&self, app: &App) -> bool { - app.world().get_resource::().is_some() - } - - fn finish(&self, app: &mut App) { - let window_render_context = WindowRenderContext::from(app as &App); - app.world_mut().insert_resource(window_render_context); - } } diff --git a/crates/engine_window/src/lib.rs b/crates/engine_window/src/lib.rs index 1576e63..33e150c 100644 --- a/crates/engine_window/src/lib.rs +++ b/crates/engine_window/src/lib.rs @@ -1,6 +1,6 @@ use bevy_app::{App, AppExit, Plugin, PluginsState}; use config::WindowConfig; -use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; +use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper, WindowWrapper}; use state::WindowState; use winit::event_loop::EventLoop; diff --git a/crates/engine_window/src/raw_handle.rs b/crates/engine_window/src/raw_handle.rs index c896b56..87d5988 100644 --- a/crates/engine_window/src/raw_handle.rs +++ b/crates/engine_window/src/raw_handle.rs @@ -16,8 +16,8 @@ impl EventLoopProxyWrapper { } } -#[derive(Resource)] +#[derive(Resource, Clone)] pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); -#[derive(Resource)] +#[derive(Resource, Clone)] pub struct WindowWrapper(pub Arc); diff --git a/src/game/mod.rs b/src/game/mod.rs index ff3df3c..9c5b547 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -33,6 +33,13 @@ pub fn init(app: &mut App) { VulkanPlugin { vulkan_config }, RenderPlugin, )); - // Window::new(app, window_config).unwrap(); - // Vulkan::new(app).unwrap(); + + // app.get_sub_app_mut(RenderApp) + // .expect("Failed to get RenderApp. Check is RenderPlugin is added.") + // .add_systems(Render, test_system.in_set(RenderSystems::Prepare)); } + +// fn test_system(vulkan_device: Res, vulkan_graphics_queue: Res) { +// log::trace!("vulkan_device: {:?}", vulkan_device.0); +// log::trace!("vulkan_graphics_queue: {:?}", vulkan_graphics_queue.0); +// } From 62d12f2ab827c5e6a327d10d841362b2fa19484b Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 18 May 2025 21:00:22 +0200 Subject: [PATCH 25/28] render_plugin: Autocreate swapchain and update --- crates/engine_render/src/lib.rs | 14 +- crates/engine_render/src/window/mod.rs | 197 +++++++++++++++++- crates/engine_vulkan/src/lib.rs | 3 - .../src/window_render_context.rs | 125 ----------- src/game/mod.rs | 9 - 5 files changed, 201 insertions(+), 147 deletions(-) delete mode 100644 crates/engine_vulkan/src/window_render_context.rs diff --git a/crates/engine_render/src/lib.rs b/crates/engine_render/src/lib.rs index f42013e..ccbc39d 100644 --- a/crates/engine_render/src/lib.rs +++ b/crates/engine_render/src/lib.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ }; use engine_vulkan::{ VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue, - VulkanMemoryAllocator, + VulkanInstance, VulkanMemoryAllocator, }; use engine_window::raw_handle::WindowWrapper; use window::WindowRenderPlugin; @@ -31,7 +31,7 @@ impl Render { let mut schedule = Schedule::new(Self); - schedule.configure_sets((Prepare, Queue, Render, Present).chain()); + schedule.configure_sets((ManageViews, Prepare, Queue, Render, Present).chain()); schedule } @@ -49,6 +49,7 @@ impl Plugin for RenderPlugin { let world = app.world(); world.get_resource::().is_some() + && world.get_resource::().is_some() && world.get_resource::().is_some() && world.get_resource::().is_some() && world.get_resource::().is_some() @@ -71,10 +72,6 @@ impl Plugin for RenderPlugin { app.add_plugins(WindowRenderPlugin); } - - fn cleanup(&self, app: &mut App) { - app.remove_sub_app(RenderApp); - } } fn extract_app_resources(world: &mut World, render_world: &mut World) { @@ -82,6 +79,10 @@ fn extract_app_resources(world: &mut World, render_world: &mut World) { .get_resource::() .expect("Failed to get WindowWrapper. Check is WindowPlugin is added before RenderPlugin."); + let vulkan_instance = world.get_resource::().expect( + "Failed to get Vulkan instance. Check is VulkanPlugin is added before RenderPlugin.", + ); + let vulkan_device = world .get_resource::() .expect("Failed to get Vulkan device. Check is VulkanPlugin is added before RenderPlugin."); @@ -102,6 +103,7 @@ fn extract_app_resources(world: &mut World, render_world: &mut World) { .get_resource::() .expect("Failed to get Vulkan descriptor set allocator. Check is VulkanPlugin is added before RenderPlugin."); + render_world.insert_resource(vulkan_instance.clone()); render_world.insert_resource(vulkan_device.clone()); render_world.insert_resource(vulkan_graphics_queue.clone()); render_world.insert_resource(vulkan_memory_allocator.clone()); diff --git a/crates/engine_render/src/window/mod.rs b/crates/engine_render/src/window/mod.rs index bcb3ea6..9af4184 100644 --- a/crates/engine_render/src/window/mod.rs +++ b/crates/engine_render/src/window/mod.rs @@ -1,13 +1,21 @@ use std::sync::Arc; use bevy_app::{App, Plugin}; -use bevy_ecs::resource::Resource; +use bevy_ecs::{ + resource::Resource, + schedule::IntoScheduleConfigs, + system::{Res, ResMut}, +}; +use engine_vulkan::{VulkanDevice, VulkanInstance}; +use engine_window::raw_handle::WindowWrapper; use vulkano::{ - image::view::ImageView, pipeline::graphics::viewport::Viewport, swapchain::Swapchain, - sync::GpuFuture, + image::{Image, ImageUsage, view::ImageView}, + pipeline::graphics::viewport::Viewport, + swapchain::{Surface, Swapchain, SwapchainCreateInfo}, + sync::{self, GpuFuture}, }; -use crate::RenderApp; +use super::{Render, RenderApp, RenderSystems}; pub struct WindowSurfaceData { pub swapchain: Arc, @@ -31,5 +39,186 @@ impl Plugin for WindowRenderPlugin { .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); +} diff --git a/crates/engine_vulkan/src/lib.rs b/crates/engine_vulkan/src/lib.rs index 65e9349..4f5129b 100644 --- a/crates/engine_vulkan/src/lib.rs +++ b/crates/engine_vulkan/src/lib.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use bevy_ecs::resource::Resource; -use engine_window::raw_handle::WindowWrapper; use utils::{device::create_and_insert_device, instance::create_and_insert_instance}; use vulkano::{ command_buffer::allocator::StandardCommandBufferAllocator, @@ -10,12 +9,10 @@ use vulkano::{ instance::Instance, memory::allocator::StandardMemoryAllocator, }; -use window_render_context::WindowRenderContext; use bevy_app::{App, Plugin}; mod utils; -mod window_render_context; #[derive(Resource, Clone)] pub struct VulkanInstance(pub Arc); diff --git a/crates/engine_vulkan/src/window_render_context.rs b/crates/engine_vulkan/src/window_render_context.rs deleted file mode 100644 index 95b3d90..0000000 --- a/crates/engine_vulkan/src/window_render_context.rs +++ /dev/null @@ -1,125 +0,0 @@ -use bevy_app::App; -use bevy_ecs::resource::Resource; -use engine_window::raw_handle::WindowWrapper; -use std::sync::Arc; -use vulkano::image::view::ImageView; -use vulkano::image::{Image, ImageUsage}; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; -use vulkano::sync::{self, GpuFuture}; -use vulkano::{Validated, VulkanError}; -use winit::window::Window; - -use crate::{VulkanDevice, VulkanInstance}; - -#[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 window_handle = world - .get_resource::() - .expect("Failed to find window handle"); - let vulkan_instance = world - .get_resource::() - .expect("Failed to find vulkan instance"); - let vulkan_device = world - .get_resource::() - .expect("Failed to find vulkan device"); - - 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"); - - 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() - }; - - 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_device.0.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/game/mod.rs b/src/game/mod.rs index 9c5b547..e04b2a7 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -33,13 +33,4 @@ pub fn init(app: &mut App) { VulkanPlugin { vulkan_config }, RenderPlugin, )); - - // app.get_sub_app_mut(RenderApp) - // .expect("Failed to get RenderApp. Check is RenderPlugin is added.") - // .add_systems(Render, test_system.in_set(RenderSystems::Prepare)); } - -// fn test_system(vulkan_device: Res, vulkan_graphics_queue: Res) { -// log::trace!("vulkan_device: {:?}", vulkan_device.0); -// log::trace!("vulkan_graphics_queue: {:?}", vulkan_graphics_queue.0); -// } From 7951b05ab3641e936faf0d02acd39cfdca48d1eb Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Mon, 19 May 2025 23:30:35 +0200 Subject: [PATCH 26/28] Add README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c74b5d4 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Project + +## Usefull links + +- https://vulkan-tutorial.com/fr/Introduction +- https://github.com/bwasty/vulkan-tutorial-rs From 18174e42e984de129b22c25473a268a7aea2b9ec Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 21 May 2025 13:42:36 +0200 Subject: [PATCH 27/28] render_vulkan: Fixes - Avoid create device for each physical device during support checking - Avoid to select different queue family index and add support of creating multiple queues on same queue family - Log select queue family index for each queue type --- crates/engine_vulkan/src/utils/device.rs | 115 ++++++++++++++------- crates/engine_vulkan/src/vulkan_context.rs | 76 -------------- 2 files changed, 77 insertions(+), 114 deletions(-) delete mode 100644 crates/engine_vulkan/src/vulkan_context.rs diff --git a/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/utils/device.rs index 6c4b49f..e44ac52 100644 --- a/crates/engine_vulkan/src/utils/device.rs +++ b/crates/engine_vulkan/src/utils/device.rs @@ -1,8 +1,9 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use bevy_ecs::world::World; use engine_window::raw_handle::DisplayHandleWrapper; use vulkano::{ + VulkanError, command_buffer::allocator::StandardCommandBufferAllocator, descriptor_set::allocator::StandardDescriptorSetAllocator, device::{ @@ -86,22 +87,30 @@ fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option() .expect("Failed to get VulkanInstance during vulkan plugin initialization"); - instance + let result = instance .0 .enumerate_physical_devices() .expect("Failed to enumerate physical devices") - .filter_map(|p| check_physical_device(world, &p, config)) - .min_by_key( - |p| match p.device.physical_device().properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }, - ) - .take() + .filter_map(|p| check_physical_device_support(world, &p, config).and_then(|r| Some((p, r)))) + .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, + }) + .take(); + + match result { + Some((p, (device_extensions, picked_queues_info))) => Some(create_device( + config, + &p, + device_extensions, + &picked_queues_info, + )), + None => None, + } } fn check_device_extensions_support( @@ -147,7 +156,7 @@ fn check_queues_support( let mut transfer_queue_family_index: Option = None; for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { - if config.with_graphics_queue { + if config.with_graphics_queue && graphics_queue_family_index.is_none() { let graphics_supported = queue_family_property .queue_flags .intersects(QueueFlags::GRAPHICS); @@ -169,7 +178,7 @@ fn check_queues_support( } } - if config.with_compute_queue { + if config.with_compute_queue && compute_queue_family_index.is_none() { let compute_supported = queue_family_property .queue_flags .intersects(QueueFlags::COMPUTE); @@ -179,7 +188,7 @@ fn check_queues_support( } } - if config.with_transfer_queue { + if config.with_transfer_queue && transfer_queue_family_index.is_none() { let transfer_supported = queue_family_property .queue_flags .intersects(QueueFlags::TRANSFER); @@ -193,7 +202,10 @@ fn check_queues_support( if !config.with_graphics_queue { log::debug!("\t\t[SKIPPED] Graphics queue is not required"); } else if graphics_queue_family_index.is_some() { - log::debug!("\t\t[OK] Graphics queue is supported"); + log::debug!( + "\t\t[OK] Graphics queue is supported (family index: {:?})", + graphics_queue_family_index + ); } else { log::debug!("\t\t[FAILED] Graphics queue is not supported"); return None; @@ -202,7 +214,10 @@ fn check_queues_support( if !config.with_compute_queue { log::debug!("\t\t[SKIPPED] Compute queue is not required"); } else if compute_queue_family_index.is_some() { - log::debug!("\t\t[OK] Compute queue is supported"); + log::debug!( + "\t\t[OK] Compute queue is supported (family index: {:?})", + compute_queue_family_index + ); } else { log::debug!("\t\t[FAILED] Compute queue is not supported"); return None; @@ -211,7 +226,10 @@ fn check_queues_support( if !config.with_transfer_queue { log::debug!("\t\t[SKIPPED] Transfer queue is not required"); } else if transfer_queue_family_index.is_some() { - log::debug!("\t\t[OK] Transfer queue is supported"); + log::debug!( + "\t\t[OK] Transfer queue is supported (family index: {:?})", + transfer_queue_family_index + ); } else { log::debug!("\t\t[FAILED] Transfer queue is not supported"); return None; @@ -224,11 +242,11 @@ fn check_queues_support( }) } -fn check_physical_device( +fn check_physical_device_support( world: &World, physical_device: &Arc, config: &VulkanConfig, -) -> Option { +) -> Option<(DeviceExtensions, PickedQueuesInfo)> { log::debug!("Checking physical device"); log::debug!("\tProperties"); log::debug!("\t\tName: {}", physical_device.properties().device_name); @@ -242,33 +260,54 @@ fn check_physical_device( let device_extensions = check_device_extensions_support(physical_device, config)?; let picked_queues_info = check_queues_support(world, physical_device, config)?; - let mut queue_create_infos = vec![]; + Some((device_extensions, picked_queues_info)) +} + +fn create_device( + config: &VulkanConfig, + physical_device: &Arc, + device_extensions: DeviceExtensions, + picked_queues_info: &PickedQueuesInfo, +) -> PickedDevice { + let mut queue_create_infos = HashMap::::new(); if config.with_graphics_queue { - queue_create_infos.push(QueueCreateInfo { - queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), - ..Default::default() - }); + let entry = queue_create_infos + .entry(picked_queues_info.graphics_queue_family_index.unwrap()) + .or_insert(QueueCreateInfo { + queue_family_index: picked_queues_info.graphics_queue_family_index.unwrap(), + ..Default::default() + }); + + entry.queues.push(1.0); } if config.with_compute_queue { - queue_create_infos.push(QueueCreateInfo { - queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), - ..Default::default() - }); + let entry = queue_create_infos + .entry(picked_queues_info.compute_queue_family_index.unwrap()) + .or_insert(QueueCreateInfo { + queue_family_index: picked_queues_info.compute_queue_family_index.unwrap(), + ..Default::default() + }); + + entry.queues.push(1.0); } if config.with_transfer_queue { - queue_create_infos.push(QueueCreateInfo { - queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), - ..Default::default() - }); + let entry = queue_create_infos + .entry(picked_queues_info.transfer_queue_family_index.unwrap()) + .or_insert(QueueCreateInfo { + queue_family_index: picked_queues_info.transfer_queue_family_index.unwrap(), + ..Default::default() + }); + + entry.queues.push(1.0); } let (device, mut queues) = Device::new( physical_device.clone(), DeviceCreateInfo { - queue_create_infos, + queue_create_infos: queue_create_infos.values().cloned().collect(), enabled_extensions: device_extensions, enabled_features: config.device_features, ..Default::default() @@ -292,10 +331,10 @@ fn check_physical_device( transfer_queue = queues.next(); } - Some(PickedDevice { + PickedDevice { device, graphics_queue, compute_queue, transfer_queue, - }) + } } diff --git a/crates/engine_vulkan/src/vulkan_context.rs b/crates/engine_vulkan/src/vulkan_context.rs deleted file mode 100644 index 08ff1ac..0000000 --- a/crates/engine_vulkan/src/vulkan_context.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::{any::Any, sync::Arc}; - -use bevy_app::App; -use bevy_ecs::resource::Resource; -use engine_window::raw_handle::DisplayHandleWrapper; -use vulkano::{ - 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 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 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 (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, - } - } -} From 9ea8721346c1060f95b5fd7515a814a97221b0ac Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 21 May 2025 13:46:13 +0200 Subject: [PATCH 28/28] render_vulkan: Avoid continue loop if all queues is found --- crates/engine_vulkan/src/utils/device.rs | 30 ++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/engine_vulkan/src/utils/device.rs b/crates/engine_vulkan/src/utils/device.rs index e44ac52..38bf5fd 100644 --- a/crates/engine_vulkan/src/utils/device.rs +++ b/crates/engine_vulkan/src/utils/device.rs @@ -87,7 +87,7 @@ fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option() .expect("Failed to get VulkanInstance during vulkan plugin initialization"); - let result = instance + instance .0 .enumerate_physical_devices() .expect("Failed to enumerate physical devices") @@ -100,17 +100,15 @@ fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option 4, _ => 5, }) - .take(); - - match result { - Some((p, (device_extensions, picked_queues_info))) => Some(create_device( - config, - &p, - device_extensions, - &picked_queues_info, - )), - None => None, - } + .take() + .and_then(|(p, (device_extensions, picked_queues_info))| { + Some(create_device( + config, + &p, + device_extensions, + &picked_queues_info, + )) + }) } fn check_device_extensions_support( @@ -197,6 +195,14 @@ fn check_queues_support( transfer_queue_family_index = Some(i as u32); } } + + if (!config.with_graphics_queue || graphics_queue_family_index.is_some()) + && (!config.with_compute_queue || compute_queue_family_index.is_some()) + && (!config.with_transfer_queue || transfer_queue_family_index.is_some()) + { + // We found all required queues, no need to continue iterating + break; + } } if !config.with_graphics_queue {