use std::sync::{Arc, RwLock}; use vulkano::buffer::{BufferContents, Subbuffer}; use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, SubpassEndInfo}; use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; use vulkano::device::{Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags}; use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType}; use vulkano::image::{Image, ImageCreateInfo, ImageUsage}; use vulkano::instance::Instance; use vulkano::memory::allocator::{AllocationCreateInfo, StandardMemoryAllocator}; use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::{GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo}; use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; use vulkano::pipeline::graphics::multisample::MultisampleState; use vulkano::pipeline::graphics::rasterization::RasterizationState; use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; use vulkano::render_pass::{Framebuffer, RenderPass, Subpass}; use vulkano::shader::ShaderModule; use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; use winit::window::Window; pub struct VulkanoWrapper { pub instance: Arc, pub surface: Arc, pub device: Arc, pub memory_allocator: Arc, pub command_buffer_allocator: Arc, pub descriptor_set_allocator: Arc, pub swapchain: RwLock>, pub images: Vec>, pub queues: Vec>, pub render_pass: Arc, pub viewport: Arc>, } impl VulkanoWrapper { pub fn new(instance: Arc, window: &Window, device_extensions: DeviceExtensions) -> Self { let surface = unsafe {Surface::from_window_ref(instance.clone(), window)}.unwrap(); let (physical_device, queue_family_index) = Self::select_physical_device(&instance, &surface, &device_extensions); for family in physical_device.queue_family_properties() { println!("Found a queue family with flags: ({:?}) with {} queues", family.queue_flags, family.queue_count) } let (device, queues) = Device::new( physical_device.clone(), DeviceCreateInfo { queue_create_infos: vec![QueueCreateInfo { queue_family_index, ..Default::default() }], enabled_extensions: device_extensions, ..Default::default() }, ).expect("failed to create logical device"); let caps = physical_device .surface_capabilities(&surface, Default::default()) .expect("failed to get surface capabilities"); let dimensions = window.inner_size(); let composite_alpha = caps.supported_composite_alpha.into_iter().next().unwrap(); let image_format = physical_device .surface_formats(&surface, Default::default()) .unwrap()[0] .0; 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() ) ); let (swapchain, images) = Swapchain::new( device.clone(), surface.clone(), SwapchainCreateInfo { min_image_count: caps.min_image_count + 1, // How many buffers to use in the swapchain image_format, image_extent: dimensions.into(), image_usage: ImageUsage::COLOR_ATTACHMENT, // What the images are going to be used for composite_alpha, ..Default::default() }, ).unwrap(); let queues = queues.into_iter().collect(); let render_pass = Self::get_render_pass(&device, &swapchain); let swapchain = RwLock::new(swapchain); Self { instance, surface, device, memory_allocator, command_buffer_allocator, descriptor_set_allocator, swapchain, images, queues, render_pass, viewport: Arc::new(RwLock::new(Viewport { offset: [0.0, 0.0], extent: window.inner_size().into(), depth_range: 0.0..=1.0, })) } } fn select_physical_device( instance: &Arc, surface: &Arc, device_extensions: &DeviceExtensions, ) -> (Arc, u32) { instance .enumerate_physical_devices() .expect("could not enumerate devices") .filter(|p| p.supported_extensions().contains(&device_extensions)) .filter_map(|p| { p.queue_family_properties() .iter() .enumerate() // Find the first first queue family that is suitable. // If none is found, `None` is returned to `filter_map`, // which disqualifies this physical device. .position(|(i, q)| { q.queue_flags.contains(QueueFlags::GRAPHICS) && p.surface_support(i as u32, &surface).unwrap_or(false) }) .map(|q| (p, q as u32)) }) .min_by_key(|(p, _)| match p.properties().device_type { PhysicalDeviceType::DiscreteGpu => 0, PhysicalDeviceType::IntegratedGpu => 1, PhysicalDeviceType::VirtualGpu => 2, PhysicalDeviceType::Cpu => 3, // Note that there exists `PhysicalDeviceType::Other`, however, // `PhysicalDeviceType` is a non-exhaustive enum. Thus, one should // match wildcard `_` to catch all unknown device types. _ => 4, }) .expect("no device available") } fn get_render_pass(device: &Arc, swapchain: &Swapchain) -> Arc { vulkano::single_pass_renderpass!( device.clone(), attachments: { color: { // Set the format the same as the swapchain. format: swapchain.image_format(), samples: 1, load_op: Clear, store_op: Store, }, }, pass: { color: [color], depth_stencil: {}, }, ).unwrap() } pub fn create_image(&self, image_create_info: ImageCreateInfo, allocation_create_info: AllocationCreateInfo) -> Arc { Image::new( self.memory_allocator.clone(), image_create_info, allocation_create_info, ).unwrap() } pub fn get_pipeline(&self, vs: Arc, fs: Arc, rp: Arc, vp: Viewport) -> Arc { get_pipeline(self.device.clone(), vs, fs, rp, vp) } pub fn get_command_buffers(&self, queue: &Arc, pipeline: &Arc, framebuffers: &Vec>, vb: &Subbuffer<[MyVertex]>) -> Vec> { get_command_buffers(self.command_buffer_allocator.clone(), queue, pipeline, framebuffers, vb) } } fn get_pipeline( device: Arc, vs: Arc, fs: Arc, render_pass: Arc, viewport: Viewport, ) -> Arc { let vs = vs.entry_point("main").unwrap(); let fs = fs.entry_point("main").unwrap(); let vertex_input_state = MyVertex::per_vertex() .definition(&vs) .unwrap(); let stages = [ PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; let layout = PipelineLayout::new( device.clone(), PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) .into_pipeline_layout_create_info(device.clone()) .unwrap(), ) .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); 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 { viewports: [viewport].into_iter().collect(), ..Default::default() }), rasterization_state: Some(RasterizationState::default()), multisample_state: Some(MultisampleState::default()), color_blend_state: Some(ColorBlendState::with_attachment_states( subpass.num_color_attachments(), ColorBlendAttachmentState::default(), )), subpass: Some(subpass.into()), ..GraphicsPipelineCreateInfo::layout(layout) }, ) .unwrap() } fn get_command_buffers( command_buffer_allocator: Arc, queue: &Arc, pipeline: &Arc, framebuffers: &Vec>, vertex_buffer: &Subbuffer<[MyVertex]>, ) -> Vec> { framebuffers .iter() .map(|framebuffer| { let mut builder = AutoCommandBufferBuilder::primary( command_buffer_allocator.clone(), queue.queue_family_index(), // Don't forget to write the correct buffer usage. CommandBufferUsage::MultipleSubmit, ) .unwrap(); builder .begin_render_pass( RenderPassBeginInfo { clear_values: vec![Some([0.1, 0.1, 0.1, 1.0].into())], ..RenderPassBeginInfo::framebuffer(framebuffer.clone()) }, SubpassBeginInfo { contents: SubpassContents::Inline, ..Default::default() }, ) .unwrap() .bind_pipeline_graphics(pipeline.clone()) .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) .unwrap(); unsafe { builder .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap(); } builder .end_render_pass(SubpassEndInfo::default()) .unwrap(); builder.build().unwrap() }) .collect() } #[derive(BufferContents, Vertex)] #[repr(C)] pub struct MyVertex { #[format(R32G32_SFLOAT)] pub position: [f32; 2], }