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(), ) } }