commit 1d23169dcc27aa085c7efb53b99d93c9dd894b7e Author: buckn Date: Thu Dec 14 10:05:03 2023 -0500 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e46f08a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tri" +version = "0.1.0" +edition = "2021" + +[dependencies] +winit = "0.20.0" +ash = "0.29.0" +num = "0.2" +cgmath = "0.17.0" +image = "0.22" +memoffset = "0.5.1" +tobj = "0.1.10" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..70dbaa8 --- /dev/null +++ b/build.rs @@ -0,0 +1,49 @@ +use std::process::Command; +use std::path::Path; +use std::ffi::OsStr; +use std::fs::File; +use std::io::Write; +use std::io::prelude::*; + +fn main() -> std::io::Result<()> { + println!("building shaders..."); + //shaders path + let shaders = Path::new("./src/shaders"); + //shader target path + let out = Command::new("mkdir") + .arg("target/shaders/") + .output(); + + let shader_target = Path::new("./target/shaders/"); + //compile all glsl shaders + for entry in shaders.read_dir().expect("reading shader directory failed") { + if let Ok(entry) = entry { + let shader_path = entry.path(); + println!("compiling shader: {:?}", shader_path); + let shader_path_string: String = "./target/shaders/".to_string() + shader_path.file_name().unwrap().to_str().unwrap() + ".spv"; + let shader_file: &OsStr = OsStr::new::(shader_path_string.as_str()); + let out = Command::new("glslc") + .arg("-c") + .arg(shader_path) + .arg("-o") + .arg(shader_file) + .output(); + } + } + //include all compiled shaders in shaders.rs file in src dir + let mut txt: String = String::new(); + for entry in shader_target.read_dir().expect("reading compiled shader target directory failed") { + if let Ok(entry) = entry { + let bin_path = entry.path(); + let bin_path_string = bin_path.file_name().unwrap().to_str().unwrap().to_string(); + txt += &("const ".to_owned() + + &bin_path_string.replace(".spv", "").replace(".", "_").to_uppercase() + + " = include_bytes!(\"../target/shaders/" + + &bin_path_string + + "\");\n"); + } + } + let mut file = File::create("./src/shaders.rs")?; + file.write_all(txt.as_bytes())?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8334dde --- /dev/null +++ b/src/main.rs @@ -0,0 +1,887 @@ +pub mod utility; + +use crate::{ + utility::constants::*, + utility::debug::*, + utility::share, + utility::structures::*, +}; + +use ash::version::DeviceV1_0; +use ash::version::InstanceV1_0; +use ash::vk; +use memoffset::offset_of; +use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; + +use std::ffi::CString; +use std::ptr; + +// Constants +const WINDOW_TITLE: &'static str = "18.Vertex Buffer"; + +#[repr(C)] +#[derive(Clone, Debug, Copy)] +struct Vertex { + pos: [f32; 2], + color: [f32; 3], +} +impl Vertex { + fn get_binding_description() -> [vk::VertexInputBindingDescription; 1] { + [vk::VertexInputBindingDescription { + binding: 0, + stride: std::mem::size_of::() as u32, + input_rate: vk::VertexInputRate::VERTEX, + }] + } + + fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { + [ + vk::VertexInputAttributeDescription { + binding: 0, + location: 0, + format: vk::Format::R32G32_SFLOAT, + offset: offset_of!(Vertex, pos) as u32, + }, + vk::VertexInputAttributeDescription { + binding: 0, + location: 1, + format: vk::Format::R32G32B32_SFLOAT, + offset: offset_of!(Vertex, color) as u32, + }, + ] + } +} + +const VERTICES_DATA: [Vertex; 3] = [ + Vertex { + pos: [0.0, -0.5], + color: [1.0, 1.0, 1.0], + }, + Vertex { + pos: [0.5, 0.5], + color: [0.0, 1.0, 0.0], + }, + Vertex { + pos: [-0.5, 0.5], + color: [0.0, 0.0, 1.0], + }, +]; + +struct VulkanApp { + window: winit::window::Window, + + // vulkan stuff + _entry: ash::Entry, + instance: ash::Instance, + surface_loader: ash::extensions::khr::Surface, + surface: vk::SurfaceKHR, + debug_utils_loader: ash::extensions::ext::DebugUtils, + debug_merssager: vk::DebugUtilsMessengerEXT, + + physical_device: vk::PhysicalDevice, + device: ash::Device, + + queue_family: QueueFamilyIndices, + graphics_queue: vk::Queue, + present_queue: vk::Queue, + + swapchain_loader: ash::extensions::khr::Swapchain, + swapchain: vk::SwapchainKHR, + swapchain_images: Vec, + swapchain_format: vk::Format, + swapchain_extent: vk::Extent2D, + swapchain_imageviews: Vec, + swapchain_framebuffers: Vec, + + render_pass: vk::RenderPass, + pipeline_layout: vk::PipelineLayout, + graphics_pipeline: vk::Pipeline, + + vertex_buffer: vk::Buffer, + vertex_buffer_memory: vk::DeviceMemory, + + command_pool: vk::CommandPool, + command_buffers: Vec, + + image_available_semaphores: Vec, + render_finished_semaphores: Vec, + in_flight_fences: Vec, + current_frame: usize, + + is_framebuffer_resized: bool, +} + +impl VulkanApp { + pub fn new(event_loop: &winit::event_loop::EventLoop<()>) -> VulkanApp { + + let window = utility::window::init_window(event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); + + // init vulkan stuff + let entry = ash::Entry::new().unwrap(); + let instance = share::create_instance( + &entry, + WINDOW_TITLE, + VALIDATION.is_enable, + &VALIDATION.required_validation_layers.to_vec(), + ); + let surface_stuff = + share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); + let (debug_utils_loader, debug_merssager) = + setup_debug_utils(VALIDATION.is_enable, &entry, &instance); + let physical_device = + share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); + let (device, queue_family) = share::create_logical_device( + &instance, + physical_device, + &VALIDATION, + &DEVICE_EXTENSIONS, + &surface_stuff, + ); + let graphics_queue = + unsafe { device.get_device_queue(queue_family.graphics_family.unwrap(), 0) }; + let present_queue = + unsafe { device.get_device_queue(queue_family.present_family.unwrap(), 0) }; + let swapchain_stuff = share::create_swapchain( + &instance, + &device, + physical_device, + &window, + &surface_stuff, + &queue_family, + ); + let swapchain_imageviews = share::v1::create_image_views( + &device, + swapchain_stuff.swapchain_format, + &swapchain_stuff.swapchain_images, + ); + let render_pass = share::v1::create_render_pass(&device, swapchain_stuff.swapchain_format); + let (graphics_pipeline, pipeline_layout) = VulkanApp::create_graphics_pipeline( + &device, + render_pass, + swapchain_stuff.swapchain_extent, + ); + let swapchain_framebuffers = share::v1::create_framebuffers( + &device, + render_pass, + &swapchain_imageviews, + swapchain_stuff.swapchain_extent, + ); + let command_pool = share::v1::create_command_pool(&device, &queue_family); + let (vertex_buffer, vertex_buffer_memory) = + VulkanApp::create_vertex_buffer(&instance, &device, physical_device); + let command_buffers = VulkanApp::create_command_buffers( + &device, + command_pool, + graphics_pipeline, + &swapchain_framebuffers, + render_pass, + swapchain_stuff.swapchain_extent, + vertex_buffer, + ); + let sync_ojbects = share::v1::create_sync_objects(&device, MAX_FRAMES_IN_FLIGHT); + + // cleanup(); the 'drop' function will take care of it. + VulkanApp { + // winit stuff + window, + + // vulkan stuff + _entry: entry, + instance, + surface: surface_stuff.surface, + surface_loader: surface_stuff.surface_loader, + debug_utils_loader, + debug_merssager, + + physical_device, + device, + + queue_family, + graphics_queue, + present_queue, + + swapchain_loader: swapchain_stuff.swapchain_loader, + swapchain: swapchain_stuff.swapchain, + swapchain_format: swapchain_stuff.swapchain_format, + swapchain_images: swapchain_stuff.swapchain_images, + swapchain_extent: swapchain_stuff.swapchain_extent, + swapchain_imageviews, + swapchain_framebuffers, + + pipeline_layout, + render_pass, + graphics_pipeline, + + vertex_buffer, + vertex_buffer_memory, + + command_pool, + command_buffers, + + image_available_semaphores: sync_ojbects.image_available_semaphores, + render_finished_semaphores: sync_ojbects.render_finished_semaphores, + in_flight_fences: sync_ojbects.inflight_fences, + current_frame: 0, + + is_framebuffer_resized: false, + } + } + + fn create_vertex_buffer( + instance: &ash::Instance, + device: &ash::Device, + physical_device: vk::PhysicalDevice, + ) -> (vk::Buffer, vk::DeviceMemory) { + let vertex_buffer_create_info = vk::BufferCreateInfo { + s_type: vk::StructureType::BUFFER_CREATE_INFO, + p_next: ptr::null(), + flags: vk::BufferCreateFlags::empty(), + size: std::mem::size_of_val(&VERTICES_DATA) as u64, + usage: vk::BufferUsageFlags::VERTEX_BUFFER, + sharing_mode: vk::SharingMode::EXCLUSIVE, + queue_family_index_count: 0, + p_queue_family_indices: ptr::null(), + }; + + let vertex_buffer = unsafe { + device + .create_buffer(&vertex_buffer_create_info, None) + .expect("Failed to create Vertex Buffer") + }; + + let mem_requirements = unsafe { device.get_buffer_memory_requirements(vertex_buffer) }; + let mem_properties = + unsafe { instance.get_physical_device_memory_properties(physical_device) }; + let required_memory_flags: vk::MemoryPropertyFlags = + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT; + let memory_type = VulkanApp::find_memory_type( + mem_requirements.memory_type_bits, + required_memory_flags, + mem_properties, + ); + + let allocate_info = vk::MemoryAllocateInfo { + s_type: vk::StructureType::MEMORY_ALLOCATE_INFO, + p_next: ptr::null(), + allocation_size: mem_requirements.size, + memory_type_index: memory_type, + }; + + let vertex_buffer_memory = unsafe { + device + .allocate_memory(&allocate_info, None) + .expect("Failed to allocate vertex buffer memory!") + }; + + unsafe { + device + .bind_buffer_memory(vertex_buffer, vertex_buffer_memory, 0) + .expect("Failed to bind Buffer"); + + let data_ptr = device + .map_memory( + vertex_buffer_memory, + 0, + vertex_buffer_create_info.size, + vk::MemoryMapFlags::empty(), + ) + .expect("Failed to Map Memory") as *mut Vertex; + + data_ptr.copy_from_nonoverlapping(VERTICES_DATA.as_ptr(), VERTICES_DATA.len()); + + device.unmap_memory(vertex_buffer_memory); + } + + (vertex_buffer, vertex_buffer_memory) + } + + fn find_memory_type( + type_filter: u32, + required_properties: vk::MemoryPropertyFlags, + mem_properties: vk::PhysicalDeviceMemoryProperties, + ) -> u32 { + for (i, memory_type) in mem_properties.memory_types.iter().enumerate() { + //if (type_filter & (1 << i)) > 0 && (memory_type.property_flags & required_properties) == required_properties { + // return i as u32 + // } + + // same implementation + if (type_filter & (1 << i)) > 0 + && memory_type.property_flags.contains(required_properties) + { + return i as u32; + } + } + + panic!("Failed to find suitable memory type!") + } + + fn create_command_buffers( + device: &ash::Device, + command_pool: vk::CommandPool, + graphics_pipeline: vk::Pipeline, + framebuffers: &Vec, + render_pass: vk::RenderPass, + surface_extent: vk::Extent2D, + vertex_buffer: vk::Buffer, + ) -> Vec { + let command_buffer_allocate_info = vk::CommandBufferAllocateInfo { + s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, + p_next: ptr::null(), + command_buffer_count: framebuffers.len() as u32, + command_pool, + level: vk::CommandBufferLevel::PRIMARY, + }; + + let command_buffers = unsafe { + device + .allocate_command_buffers(&command_buffer_allocate_info) + .expect("Failed to allocate Command Buffers!") + }; + + for (i, &command_buffer) in command_buffers.iter().enumerate() { + let command_buffer_begin_info = vk::CommandBufferBeginInfo { + s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO, + p_next: ptr::null(), + flags: vk::CommandBufferUsageFlags::SIMULTANEOUS_USE, + p_inheritance_info: ptr::null(), + }; + + unsafe { + device + .begin_command_buffer(command_buffer, &command_buffer_begin_info) + .expect("Failed to begin recording Command Buffer at beginning!"); + } + + let clear_values = [vk::ClearValue { + color: vk::ClearColorValue { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }]; + + let render_pass_begin_info = vk::RenderPassBeginInfo { + s_type: vk::StructureType::RENDER_PASS_BEGIN_INFO, + p_next: ptr::null(), + framebuffer: framebuffers[i], + render_pass, + clear_value_count: clear_values.len() as u32, + p_clear_values: clear_values.as_ptr(), + render_area: vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: surface_extent, + }, + }; + + unsafe { + device.cmd_begin_render_pass( + command_buffer, + &render_pass_begin_info, + vk::SubpassContents::INLINE, + ); + device.cmd_bind_pipeline( + command_buffer, + vk::PipelineBindPoint::GRAPHICS, + graphics_pipeline, + ); + + let vertex_buffers = [vertex_buffer]; + let offsets = [0_u64]; + + device.cmd_bind_vertex_buffers(command_buffer, 0, &vertex_buffers, &offsets); + + device.cmd_draw(command_buffer, VERTICES_DATA.len() as u32, 1, 0, 0); + + device.cmd_end_render_pass(command_buffer); + + device + .end_command_buffer(command_buffer) + .expect("Failed to record Command Buffer at Ending!"); + } + } + + command_buffers + } +} + +// Fix content ------------------------------------------------------------------------------- +impl VulkanApp { + fn create_graphics_pipeline( + device: &ash::Device, + render_pass: vk::RenderPass, + swapchain_extent: vk::Extent2D, + ) -> (vk::Pipeline, vk::PipelineLayout) { + let vert_shader_module = share::create_shader_module( + device, + include_bytes!("../target/shaders/tri.vert.spv").to_vec(), + ); + let frag_shader_module = share::create_shader_module( + device, + include_bytes!("../target/shaders/tri.frag.spv").to_vec(), + ); + + let main_function_name = CString::new("main").unwrap(); // the beginning function name in shader code. + + let shader_stages = [ + vk::PipelineShaderStageCreateInfo { + // Vertex Shader + s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineShaderStageCreateFlags::empty(), + module: vert_shader_module, + p_name: main_function_name.as_ptr(), + stage: vk::ShaderStageFlags::VERTEX, + p_specialization_info: ptr::null(), + }, + vk::PipelineShaderStageCreateInfo { + // Fragment Shader + s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineShaderStageCreateFlags::empty(), + module: frag_shader_module, + p_name: main_function_name.as_ptr(), + stage: vk::ShaderStageFlags::FRAGMENT, + p_specialization_info: ptr::null(), + }, + ]; + + let binding_description = Vertex::get_binding_description(); + let attribute_description = Vertex::get_attribute_descriptions(); + + let vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo { + s_type: vk::StructureType::PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineVertexInputStateCreateFlags::empty(), + vertex_attribute_description_count: attribute_description.len() as u32, + p_vertex_attribute_descriptions: attribute_description.as_ptr(), + vertex_binding_description_count: binding_description.len() as u32, + p_vertex_binding_descriptions: binding_description.as_ptr(), + }; + let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo { + s_type: vk::StructureType::PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + flags: vk::PipelineInputAssemblyStateCreateFlags::empty(), + p_next: ptr::null(), + topology: vk::PrimitiveTopology::TRIANGLE_LIST, + primitive_restart_enable: vk::FALSE, + }; + + let viewports = [vk::Viewport { + x: 0.0, + y: 0.0, + width: swapchain_extent.width as f32, + height: swapchain_extent.height as f32, + min_depth: 0.0, + max_depth: 1.0, + }]; + + let scissors = [vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: swapchain_extent, + }]; + + let viewport_state_create_info = vk::PipelineViewportStateCreateInfo { + s_type: vk::StructureType::PIPELINE_VIEWPORT_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineViewportStateCreateFlags::empty(), + scissor_count: scissors.len() as u32, + p_scissors: scissors.as_ptr(), + viewport_count: viewports.len() as u32, + p_viewports: viewports.as_ptr(), + }; + + let rasterization_statue_create_info = vk::PipelineRasterizationStateCreateInfo { + s_type: vk::StructureType::PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineRasterizationStateCreateFlags::empty(), + cull_mode: vk::CullModeFlags::BACK, + front_face: vk::FrontFace::CLOCKWISE, + line_width: 1.0, + polygon_mode: vk::PolygonMode::FILL, + rasterizer_discard_enable: vk::FALSE, + depth_clamp_enable: vk::FALSE, + depth_bias_clamp: 0.0, + depth_bias_constant_factor: 0.0, + depth_bias_enable: vk::FALSE, + depth_bias_slope_factor: 0.0, + }; + + let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo { + s_type: vk::StructureType::PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + flags: vk::PipelineMultisampleStateCreateFlags::empty(), + p_next: ptr::null(), + rasterization_samples: vk::SampleCountFlags::TYPE_1, + sample_shading_enable: vk::FALSE, + min_sample_shading: 0.0, + p_sample_mask: ptr::null(), + alpha_to_one_enable: vk::FALSE, + alpha_to_coverage_enable: vk::FALSE, + }; + + let stencil_state = vk::StencilOpState { + fail_op: vk::StencilOp::KEEP, + pass_op: vk::StencilOp::KEEP, + depth_fail_op: vk::StencilOp::KEEP, + compare_op: vk::CompareOp::ALWAYS, + compare_mask: 0, + write_mask: 0, + reference: 0, + }; + + let depth_state_create_info = vk::PipelineDepthStencilStateCreateInfo { + s_type: vk::StructureType::PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineDepthStencilStateCreateFlags::empty(), + depth_test_enable: vk::FALSE, + depth_write_enable: vk::FALSE, + depth_compare_op: vk::CompareOp::LESS_OR_EQUAL, + depth_bounds_test_enable: vk::FALSE, + stencil_test_enable: vk::FALSE, + front: stencil_state, + back: stencil_state, + max_depth_bounds: 1.0, + min_depth_bounds: 0.0, + }; + + let color_blend_attachment_states = [vk::PipelineColorBlendAttachmentState { + blend_enable: vk::FALSE, + color_write_mask: vk::ColorComponentFlags::all(), + src_color_blend_factor: vk::BlendFactor::ONE, + dst_color_blend_factor: vk::BlendFactor::ZERO, + color_blend_op: vk::BlendOp::ADD, + src_alpha_blend_factor: vk::BlendFactor::ONE, + dst_alpha_blend_factor: vk::BlendFactor::ZERO, + alpha_blend_op: vk::BlendOp::ADD, + }]; + + let color_blend_state = vk::PipelineColorBlendStateCreateInfo { + s_type: vk::StructureType::PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineColorBlendStateCreateFlags::empty(), + logic_op_enable: vk::FALSE, + logic_op: vk::LogicOp::COPY, + attachment_count: color_blend_attachment_states.len() as u32, + p_attachments: color_blend_attachment_states.as_ptr(), + blend_constants: [0.0, 0.0, 0.0, 0.0], + }; + + let pipeline_layout_create_info = vk::PipelineLayoutCreateInfo { + s_type: vk::StructureType::PIPELINE_LAYOUT_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineLayoutCreateFlags::empty(), + set_layout_count: 0, + p_set_layouts: ptr::null(), + push_constant_range_count: 0, + p_push_constant_ranges: ptr::null(), + }; + + let pipeline_layout = unsafe { + device + .create_pipeline_layout(&pipeline_layout_create_info, None) + .expect("Failed to create pipeline layout!") + }; + + let graphic_pipeline_create_infos = [vk::GraphicsPipelineCreateInfo { + s_type: vk::StructureType::GRAPHICS_PIPELINE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineCreateFlags::empty(), + stage_count: shader_stages.len() as u32, + p_stages: shader_stages.as_ptr(), + p_vertex_input_state: &vertex_input_state_create_info, + p_input_assembly_state: &vertex_input_assembly_state_info, + p_tessellation_state: ptr::null(), + p_viewport_state: &viewport_state_create_info, + p_rasterization_state: &rasterization_statue_create_info, + p_multisample_state: &multisample_state_create_info, + p_depth_stencil_state: &depth_state_create_info, + p_color_blend_state: &color_blend_state, + p_dynamic_state: ptr::null(), + layout: pipeline_layout, + render_pass, + subpass: 0, + base_pipeline_handle: vk::Pipeline::null(), + base_pipeline_index: -1, + }]; + + let graphics_pipelines = unsafe { + device + .create_graphics_pipelines( + vk::PipelineCache::null(), + &graphic_pipeline_create_infos, + None, + ) + .expect("Failed to create Graphics Pipeline!.") + }; + + unsafe { + device.destroy_shader_module(vert_shader_module, None); + device.destroy_shader_module(frag_shader_module, None); + } + + (graphics_pipelines[0], pipeline_layout) + } + + fn draw_frame(&mut self) { + let wait_fences = [self.in_flight_fences[self.current_frame]]; + + unsafe { + self.device + .wait_for_fences(&wait_fences, true, std::u64::MAX) + .expect("Failed to wait for Fence!"); + } + + let (image_index, _is_sub_optimal) = unsafe { + let result = self.swapchain_loader.acquire_next_image( + self.swapchain, + std::u64::MAX, + self.image_available_semaphores[self.current_frame], + vk::Fence::null(), + ); + match result { + Ok(image_index) => image_index, + Err(vk_result) => match vk_result { + vk::Result::ERROR_OUT_OF_DATE_KHR => { + self.recreate_swapchain(); + return; + } + _ => panic!("Failed to acquire Swap Chain Image!"), + }, + } + }; + + let wait_semaphores = [self.image_available_semaphores[self.current_frame]]; + let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; + let signal_semaphores = [self.render_finished_semaphores[self.current_frame]]; + + let submit_infos = [vk::SubmitInfo { + s_type: vk::StructureType::SUBMIT_INFO, + p_next: ptr::null(), + wait_semaphore_count: wait_semaphores.len() as u32, + p_wait_semaphores: wait_semaphores.as_ptr(), + p_wait_dst_stage_mask: wait_stages.as_ptr(), + command_buffer_count: 1, + p_command_buffers: &self.command_buffers[image_index as usize], + signal_semaphore_count: signal_semaphores.len() as u32, + p_signal_semaphores: signal_semaphores.as_ptr(), + }]; + + unsafe { + self.device + .reset_fences(&wait_fences) + .expect("Failed to reset Fence!"); + + self.device + .queue_submit( + self.graphics_queue, + &submit_infos, + self.in_flight_fences[self.current_frame], + ) + .expect("Failed to execute queue submit."); + } + + let swapchains = [self.swapchain]; + + let present_info = vk::PresentInfoKHR { + s_type: vk::StructureType::PRESENT_INFO_KHR, + p_next: ptr::null(), + wait_semaphore_count: 1, + p_wait_semaphores: signal_semaphores.as_ptr(), + swapchain_count: 1, + p_swapchains: swapchains.as_ptr(), + p_image_indices: &image_index, + p_results: ptr::null_mut(), + }; + + let result = unsafe { + self.swapchain_loader + .queue_present(self.present_queue, &present_info) + }; + + let is_resized = match result { + Ok(_) => self.is_framebuffer_resized, + Err(vk_result) => match vk_result { + vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR => true, + _ => panic!("Failed to execute queue present."), + }, + }; + if is_resized { + self.is_framebuffer_resized = false; + self.recreate_swapchain(); + } + + self.current_frame = (self.current_frame + 1) % MAX_FRAMES_IN_FLIGHT; + } + + fn recreate_swapchain(&mut self) { + // parameters ------------- + let surface_suff = SurfaceStuff { + surface_loader: self.surface_loader.clone(), + surface: self.surface, + screen_width: WINDOW_WIDTH, + screen_height: WINDOW_HEIGHT, + }; + // ------------------------ + + unsafe { + self.device + .device_wait_idle() + .expect("Failed to wait device idle!") + }; + self.cleanup_swapchain(); + + let swapchain_stuff = share::create_swapchain( + &self.instance, + &self.device, + self.physical_device, + &self.window, + &surface_suff, + &self.queue_family, + ); + self.swapchain_loader = swapchain_stuff.swapchain_loader; + self.swapchain = swapchain_stuff.swapchain; + self.swapchain_images = swapchain_stuff.swapchain_images; + self.swapchain_format = swapchain_stuff.swapchain_format; + self.swapchain_extent = swapchain_stuff.swapchain_extent; + + self.swapchain_imageviews = share::v1::create_image_views( + &self.device, + self.swapchain_format, + &self.swapchain_images, + ); + self.render_pass = share::v1::create_render_pass(&self.device, self.swapchain_format); + let (graphics_pipeline, pipeline_layout) = VulkanApp::create_graphics_pipeline( + &self.device, + self.render_pass, + swapchain_stuff.swapchain_extent, + ); + self.graphics_pipeline = graphics_pipeline; + self.pipeline_layout = pipeline_layout; + + self.swapchain_framebuffers = share::v1::create_framebuffers( + &self.device, + self.render_pass, + &self.swapchain_imageviews, + self.swapchain_extent, + ); + self.command_buffers = VulkanApp::create_command_buffers( + &self.device, + self.command_pool, + self.graphics_pipeline, + &self.swapchain_framebuffers, + self.render_pass, + self.swapchain_extent, + self.vertex_buffer, + ); + } + + fn cleanup_swapchain(&self) { + unsafe { + self.device + .free_command_buffers(self.command_pool, &self.command_buffers); + for &framebuffer in self.swapchain_framebuffers.iter() { + self.device.destroy_framebuffer(framebuffer, None); + } + self.device.destroy_pipeline(self.graphics_pipeline, None); + self.device + .destroy_pipeline_layout(self.pipeline_layout, None); + self.device.destroy_render_pass(self.render_pass, None); + for &image_view in self.swapchain_imageviews.iter() { + self.device.destroy_image_view(image_view, None); + } + self.swapchain_loader + .destroy_swapchain(self.swapchain, None); + } + } +} + +impl Drop for VulkanApp { + fn drop(&mut self) { + unsafe { + for i in 0..MAX_FRAMES_IN_FLIGHT { + self.device + .destroy_semaphore(self.image_available_semaphores[i], None); + self.device + .destroy_semaphore(self.render_finished_semaphores[i], None); + self.device.destroy_fence(self.in_flight_fences[i], None); + } + + self.cleanup_swapchain(); + + self.device.destroy_buffer(self.vertex_buffer, None); + self.device.free_memory(self.vertex_buffer_memory, None); + + self.device.destroy_command_pool(self.command_pool, None); + + self.device.destroy_device(None); + self.surface_loader.destroy_surface(self.surface, None); + + if VALIDATION.is_enable { + self.debug_utils_loader + .destroy_debug_utils_messenger(self.debug_merssager, None); + } + self.instance.destroy_instance(None); + } + } +} + + +impl VulkanApp { + + pub fn main_loop(mut self, event_loop: EventLoop<()>) { + + let mut tick_counter = utility::fps_limiter::FPSLimiter::new(); + + event_loop.run(move |event, _, control_flow| { + + match event { + | Event::WindowEvent { event, .. } => { + match event { + | WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit + }, + | WindowEvent::KeyboardInput { input, .. } => { + match input { + | KeyboardInput { virtual_keycode, state, .. } => { + match (virtual_keycode, state) { + | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { + *control_flow = ControlFlow::Exit + }, + | _ => {}, + } + }, + } + }, + | _ => {}, + } + }, + | Event::MainEventsCleared => { + self.window.request_redraw(); + }, + | Event::RedrawRequested(_window_id) => { + self.draw_frame(); + + tick_counter.tick_frame(); + if IS_PAINT_FPS_COUNTER { + print!("FPS: {}\r", tick_counter.fps()); + } + }, + | Event::LoopDestroyed => { + unsafe { + self.device.device_wait_idle() + .expect("Failed to wait device idle!") + }; + }, + _ => (), + } + + }) + } +} + +fn main() { + + let event_loop = EventLoop::new(); + + let vulkan_app = VulkanApp::new(&event_loop); + vulkan_app.main_loop(event_loop); +} +// ------------------------------------------------------------------------------------------- diff --git a/src/shaders.rs b/src/shaders.rs new file mode 100644 index 0000000..1c306c7 --- /dev/null +++ b/src/shaders.rs @@ -0,0 +1,2 @@ +const TRI_FRAG = include_bytes!("../target/shaders/tri.frag.spv"); +const TRI_VERT = include_bytes!("../target/shaders/tri.vert.spv"); diff --git a/src/shaders/tri.frag b/src/shaders/tri.frag new file mode 100644 index 0000000..c64de63 --- /dev/null +++ b/src/shaders/tri.frag @@ -0,0 +1,12 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects: enable + +layout (location = 0) in vec3 fragColor; + +layout (location = 0) out vec4 outColor; + +void main() { + + outColor = vec4(fragColor, 1.0); +} diff --git a/src/shaders/tri.vert b/src/shaders/tri.vert new file mode 100644 index 0000000..78a941a --- /dev/null +++ b/src/shaders/tri.vert @@ -0,0 +1,18 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects: enable + +layout (location = 0) in vec2 inPosition; +layout (location = 1) in vec3 inColor; + +layout (location = 0) out vec3 fragColor; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; +} diff --git a/src/utility.rs b/src/utility.rs new file mode 100644 index 0000000..7aa301a --- /dev/null +++ b/src/utility.rs @@ -0,0 +1,8 @@ +pub mod constants; +pub mod debug; +pub mod fps_limiter; +pub mod platforms; +pub mod share; +pub mod structures; +pub mod tools; +pub mod window; diff --git a/src/utility/constants.rs b/src/utility/constants.rs new file mode 100644 index 0000000..3871f91 --- /dev/null +++ b/src/utility/constants.rs @@ -0,0 +1,30 @@ +use crate::utility::debug::ValidationInfo; +use crate::utility::structures::DeviceExtension; +use ash::vk_make_version; + +use std::os::raw::c_char; + +pub const APPLICATION_VERSION: u32 = vk_make_version!(1, 0, 0); +pub const ENGINE_VERSION: u32 = vk_make_version!(1, 0, 0); +pub const API_VERSION: u32 = vk_make_version!(1, 0, 92); + +pub const WINDOW_WIDTH: u32 = 800; +pub const WINDOW_HEIGHT: u32 = 600; +pub const VALIDATION: ValidationInfo = ValidationInfo { + is_enable: true, + required_validation_layers: ["VK_LAYER_KHRONOS_validation"], +}; +pub const DEVICE_EXTENSIONS: DeviceExtension = DeviceExtension { + names: ["VK_KHR_swapchain"], +}; +pub const MAX_FRAMES_IN_FLIGHT: usize = 2; +pub const IS_PAINT_FPS_COUNTER: bool = false; + +impl DeviceExtension { + pub fn get_extensions_raw_names(&self) -> [*const c_char; 1] { + [ + // currently just enable the Swapchain extension. + ash::extensions::khr::Swapchain::name().as_ptr(), + ] + } +} diff --git a/src/utility/debug.rs b/src/utility/debug.rs new file mode 100644 index 0000000..9c41e40 --- /dev/null +++ b/src/utility/debug.rs @@ -0,0 +1,109 @@ +use ash::version::EntryV1_0; +use ash::vk; + +use std::ffi::CStr; +use std::os::raw::c_void; +use std::ptr; + +unsafe extern "system" fn vulkan_debug_utils_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, + _p_user_data: *mut c_void, +) -> vk::Bool32 { + let severity = match message_severity { + vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => "[Verbose]", + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => "[Warning]", + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => "[Error]", + vk::DebugUtilsMessageSeverityFlagsEXT::INFO => "[Info]", + _ => "[Unknown]", + }; + let types = match message_type { + vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[General]", + vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[Performance]", + vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[Validation]", + _ => "[Unknown]", + }; + let message = CStr::from_ptr((*p_callback_data).p_message); + println!("[Debug]{}{}{:?}", severity, types, message); + + vk::FALSE +} + +pub struct ValidationInfo { + pub is_enable: bool, + pub required_validation_layers: [&'static str; 1], +} + +pub fn check_validation_layer_support( + entry: &ash::Entry, + required_validation_layers: &Vec<&str>, +) -> bool { + // if support validation layer, then return true + + let layer_properties = entry + .enumerate_instance_layer_properties() + .expect("Failed to enumerate Instance Layers Properties"); + + if layer_properties.len() <= 0 { + eprintln!("No available layers."); + return false; + } + + for required_layer_name in required_validation_layers.iter() { + let mut is_layer_found = false; + + for layer_property in layer_properties.iter() { + let test_layer_name = super::tools::vk_to_string(&layer_property.layer_name); + if (*required_layer_name) == test_layer_name { + is_layer_found = true; + break; + } + } + + if is_layer_found == false { + return false; + } + } + + true +} + +pub fn setup_debug_utils( + is_enable_debug: bool, + entry: &ash::Entry, + instance: &ash::Instance, +) -> (ash::extensions::ext::DebugUtils, vk::DebugUtilsMessengerEXT) { + let debug_utils_loader = ash::extensions::ext::DebugUtils::new(entry, instance); + + if is_enable_debug == false { + (debug_utils_loader, ash::vk::DebugUtilsMessengerEXT::null()) + } else { + let messenger_ci = populate_debug_messenger_create_info(); + + let utils_messenger = unsafe { + debug_utils_loader + .create_debug_utils_messenger(&messenger_ci, None) + .expect("Debug Utils Callback") + }; + + (debug_utils_loader, utils_messenger) + } +} + +pub fn populate_debug_messenger_create_info() -> vk::DebugUtilsMessengerCreateInfoEXT { + vk::DebugUtilsMessengerCreateInfoEXT { + s_type: vk::StructureType::DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + p_next: ptr::null(), + flags: vk::DebugUtilsMessengerCreateFlagsEXT::empty(), + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT::WARNING | + // vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE | + // vk::DebugUtilsMessageSeverityFlagsEXT::INFO | + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, + message_type: vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION, + pfn_user_callback: Some(vulkan_debug_utils_callback), + p_user_data: ptr::null_mut(), + } +} diff --git a/src/utility/fps_limiter.rs b/src/utility/fps_limiter.rs new file mode 100644 index 0000000..1b84e42 --- /dev/null +++ b/src/utility/fps_limiter.rs @@ -0,0 +1,67 @@ +use std::thread; +use std::time::Duration; +use std::time::Instant; + +const SAMPLE_COUNT: usize = 5; +const SAMPLE_COUNT_FLOAT: f32 = SAMPLE_COUNT as f32; + +pub struct FPSLimiter { + counter: Instant, + frame_time_prefer: u32, // unit microseconds + samples: [u32; SAMPLE_COUNT], + current_frame: usize, + delta_frame: u32, +} + +impl FPSLimiter { + pub fn new() -> FPSLimiter { + const DEFAULT_PREFER_FPS: f32 = 60.0; + + FPSLimiter { + counter: Instant::now(), + frame_time_prefer: (1000_000.0_f32 / DEFAULT_PREFER_FPS) as u32, + samples: [0; SAMPLE_COUNT], + current_frame: 0, + delta_frame: 0, + } + } + + pub fn set_prefer_fps(&mut self, prefer_fps: f32) { + self.frame_time_prefer = (1000_000.0_f32 / prefer_fps) as u32; + } + + /// Call this function in game loop to update its inner status. + pub fn tick_frame(&mut self) { + let time_elapsed = self.counter.elapsed(); + self.counter = Instant::now(); + + self.delta_frame = time_elapsed.subsec_micros(); + self.samples[self.current_frame] = self.delta_frame; + self.current_frame = (self.current_frame + 1) % SAMPLE_COUNT; + } + + // TODO: this function seems not work. + pub fn keep_fps(&self) { + if self.frame_time_prefer > self.delta_frame { + let delay = Duration::from_micros((self.frame_time_prefer - self.delta_frame) as u64); + + thread::sleep(delay); + } + } + + /// Calculate the current FPS. + pub fn fps(&self) -> f32 { + let mut sum = 0_u32; + self.samples.iter().for_each(|val| { + sum += val; + }); + + 1000_000.0_f32 / (sum as f32 / SAMPLE_COUNT_FLOAT) + } + + /// Return current delta time in seconds + /// this function ignore its second part, since the second is mostly zero. + pub fn delta_time(&self) -> f32 { + self.delta_frame as f32 / 1000_000.0_f32 // time in second + } +} diff --git a/src/utility/platforms.rs b/src/utility/platforms.rs new file mode 100644 index 0000000..82ab0bd --- /dev/null +++ b/src/utility/platforms.rs @@ -0,0 +1,134 @@ +use ash::version::{EntryV1_0, InstanceV1_0}; +use ash::vk; + +#[cfg(target_os = "windows")] +use ash::extensions::khr::Win32Surface; +#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] +use ash::extensions::khr::XlibSurface; +#[cfg(target_os = "macos")] +use ash::extensions::mvk::MacOSSurface; + +use ash::extensions::ext::DebugUtils; +use ash::extensions::khr::Surface; + +#[cfg(target_os = "macos")] +use cocoa::appkit::{NSView, NSWindow}; +#[cfg(target_os = "macos")] +use cocoa::base::id as cocoa_id; +#[cfg(target_os = "macos")] +use metal::CoreAnimationLayer; +#[cfg(target_os = "macos")] +use objc::runtime::YES; + +// required extension ------------------------------------------------------ +#[cfg(target_os = "macos")] +pub fn required_extension_names() -> Vec<*const i8> { + vec![ + Surface::name().as_ptr(), + MacOSSurface::name().as_ptr(), + DebugUtils::name().as_ptr(), + ] +} + +#[cfg(all(windows))] +pub fn required_extension_names() -> Vec<*const i8> { + vec![ + Surface::name().as_ptr(), + Win32Surface::name().as_ptr(), + DebugUtils::name().as_ptr(), + ] +} + +#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] +pub fn required_extension_names() -> Vec<*const i8> { + vec![ + Surface::name().as_ptr(), + XlibSurface::name().as_ptr(), + DebugUtils::name().as_ptr(), + ] +} +// ------------------------------------------------------------------------ + +// create surface --------------------------------------------------------- +#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] +pub unsafe fn create_surface( + entry: &E, + instance: &I, + window: &winit::window::Window, +) -> Result { + use std::ptr; + use winit::platform::unix::WindowExtUnix; + + let x11_display = window.xlib_display().unwrap(); + let x11_window = window.xlib_window().unwrap(); + let x11_create_info = vk::XlibSurfaceCreateInfoKHR { + s_type: vk::StructureType::XLIB_SURFACE_CREATE_INFO_KHR, + p_next: ptr::null(), + flags: Default::default(), + window: x11_window as vk::Window, + dpy: x11_display as *mut vk::Display, + }; + let xlib_surface_loader = XlibSurface::new(entry, instance); + xlib_surface_loader.create_xlib_surface(&x11_create_info, None) +} + +#[cfg(target_os = "macos")] +pub unsafe fn create_surface( + entry: &E, + instance: &I, + window: &winit::window::Window, +) -> Result { + use std::mem; + use std::os::raw::c_void; + use std::ptr; + use winit::platform::macos::WindowExtMacOS; + + let wnd: cocoa_id = mem::transmute(window.ns_window()); + + let layer = CoreAnimationLayer::new(); + + layer.set_edge_antialiasing_mask(0); + layer.set_presents_with_transaction(false); + layer.remove_all_animations(); + + let view = wnd.contentView(); + + layer.set_contents_scale(view.backingScaleFactor()); + view.setLayer(mem::transmute(layer.as_ref())); + view.setWantsLayer(YES); + + let create_info = vk::MacOSSurfaceCreateInfoMVK { + s_type: vk::StructureType::MACOS_SURFACE_CREATE_INFO_M, + p_next: ptr::null(), + flags: Default::default(), + p_view: window.ns_view() as *const c_void, + }; + + let macos_surface_loader = MacOSSurface::new(entry, instance); + macos_surface_loader.create_mac_os_surface_mvk(&create_info, None) +} + +#[cfg(target_os = "windows")] +pub unsafe fn create_surface( + entry: &E, + instance: &I, + window: &winit::window::Window, +) -> Result { + use std::os::raw::c_void; + use std::ptr; + use winapi::shared::windef::HWND; + use winapi::um::libloaderapi::GetModuleHandleW; + use winit::platform::windows::WindowExtWindows; + + let hwnd = window.hwnd() as HWND; + let hinstance = GetModuleHandleW(ptr::null()) as *const c_void; + let win32_create_info = vk::Win32SurfaceCreateInfoKHR { + s_type: vk::StructureType::WIN32_SURFACE_CREATE_INFO_KHR, + p_next: ptr::null(), + flags: Default::default(), + hinstance, + hwnd: hwnd as *const c_void, + }; + let win32_surface_loader = Win32Surface::new(entry, instance); + win32_surface_loader.create_win32_surface(&win32_create_info, None) +} diff --git a/src/utility/share.rs b/src/utility/share.rs new file mode 100644 index 0000000..f10a52d --- /dev/null +++ b/src/utility/share.rs @@ -0,0 +1,2169 @@ +pub mod v1 { + use ash::vk; + use image; + use image::GenericImageView; + + use std::cmp::max; + use std::ffi::CString; + use std::path::Path; + use std::ptr; + + use super::*; + + pub fn create_render_pass(device: &ash::Device, surface_format: vk::Format) -> vk::RenderPass { + let color_attachment = vk::AttachmentDescription { + format: surface_format, + flags: vk::AttachmentDescriptionFlags::empty(), + samples: vk::SampleCountFlags::TYPE_1, + load_op: vk::AttachmentLoadOp::CLEAR, + store_op: vk::AttachmentStoreOp::STORE, + stencil_load_op: vk::AttachmentLoadOp::DONT_CARE, + stencil_store_op: vk::AttachmentStoreOp::DONT_CARE, + initial_layout: vk::ImageLayout::UNDEFINED, + final_layout: vk::ImageLayout::PRESENT_SRC_KHR, + }; + + let color_attachment_ref = vk::AttachmentReference { + attachment: 0, + layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + }; + + let subpasses = [vk::SubpassDescription { + color_attachment_count: 1, + p_color_attachments: &color_attachment_ref, + p_depth_stencil_attachment: ptr::null(), + flags: vk::SubpassDescriptionFlags::empty(), + pipeline_bind_point: vk::PipelineBindPoint::GRAPHICS, + input_attachment_count: 0, + p_input_attachments: ptr::null(), + p_resolve_attachments: ptr::null(), + preserve_attachment_count: 0, + p_preserve_attachments: ptr::null(), + }]; + + let render_pass_attachments = [color_attachment]; + + let subpass_dependencies = [vk::SubpassDependency { + src_subpass: vk::SUBPASS_EXTERNAL, + dst_subpass: 0, + src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, + dst_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, + src_access_mask: vk::AccessFlags::empty(), + dst_access_mask: vk::AccessFlags::COLOR_ATTACHMENT_WRITE, + dependency_flags: vk::DependencyFlags::empty(), + }]; + + let renderpass_create_info = vk::RenderPassCreateInfo { + s_type: vk::StructureType::RENDER_PASS_CREATE_INFO, + flags: vk::RenderPassCreateFlags::empty(), + p_next: ptr::null(), + attachment_count: render_pass_attachments.len() as u32, + p_attachments: render_pass_attachments.as_ptr(), + subpass_count: subpasses.len() as u32, + p_subpasses: subpasses.as_ptr(), + dependency_count: subpass_dependencies.len() as u32, + p_dependencies: subpass_dependencies.as_ptr(), + }; + + unsafe { + device + .create_render_pass(&renderpass_create_info, None) + .expect("Failed to create render pass!") + } + } + + pub fn create_graphics_pipeline( + device: &ash::Device, + render_pass: vk::RenderPass, + swapchain_extent: vk::Extent2D, + ) -> (vk::Pipeline, vk::PipelineLayout) { + let vert_shader_module = create_shader_module( + device, + include_bytes!("../../target/shaders/tri.vert.spv").to_vec(), + ); + let frag_shader_module = create_shader_module( + device, + include_bytes!("../../target/shaders/tri.frag.spv").to_vec(), + ); + + let main_function_name = CString::new("main").unwrap(); // the beginning function name in shader code. + + let shader_stages = [ + vk::PipelineShaderStageCreateInfo { + // Vertex Shader + s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineShaderStageCreateFlags::empty(), + module: vert_shader_module, + p_name: main_function_name.as_ptr(), + p_specialization_info: ptr::null(), + stage: vk::ShaderStageFlags::VERTEX, + }, + vk::PipelineShaderStageCreateInfo { + // Fragment Shader + s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineShaderStageCreateFlags::empty(), + module: frag_shader_module, + p_name: main_function_name.as_ptr(), + p_specialization_info: ptr::null(), + stage: vk::ShaderStageFlags::FRAGMENT, + }, + ]; + + let vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo { + s_type: vk::StructureType::PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineVertexInputStateCreateFlags::empty(), + vertex_attribute_description_count: 0, + p_vertex_attribute_descriptions: ptr::null(), + vertex_binding_description_count: 0, + p_vertex_binding_descriptions: ptr::null(), + }; + let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo { + s_type: vk::StructureType::PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + flags: vk::PipelineInputAssemblyStateCreateFlags::empty(), + p_next: ptr::null(), + primitive_restart_enable: vk::FALSE, + topology: vk::PrimitiveTopology::TRIANGLE_LIST, + }; + + let viewports = [vk::Viewport { + x: 0.0, + y: 0.0, + width: swapchain_extent.width as f32, + height: swapchain_extent.height as f32, + min_depth: 0.0, + max_depth: 1.0, + }]; + + let scissors = [vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: swapchain_extent, + }]; + + let viewport_state_create_info = vk::PipelineViewportStateCreateInfo { + s_type: vk::StructureType::PIPELINE_VIEWPORT_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineViewportStateCreateFlags::empty(), + scissor_count: scissors.len() as u32, + p_scissors: scissors.as_ptr(), + viewport_count: viewports.len() as u32, + p_viewports: viewports.as_ptr(), + }; + + let rasterization_statue_create_info = vk::PipelineRasterizationStateCreateInfo { + s_type: vk::StructureType::PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineRasterizationStateCreateFlags::empty(), + depth_clamp_enable: vk::FALSE, + cull_mode: vk::CullModeFlags::BACK, + front_face: vk::FrontFace::CLOCKWISE, + line_width: 1.0, + polygon_mode: vk::PolygonMode::FILL, + rasterizer_discard_enable: vk::FALSE, + depth_bias_clamp: 0.0, + depth_bias_constant_factor: 0.0, + depth_bias_enable: vk::FALSE, + depth_bias_slope_factor: 0.0, + }; + let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo { + s_type: vk::StructureType::PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + flags: vk::PipelineMultisampleStateCreateFlags::empty(), + p_next: ptr::null(), + rasterization_samples: vk::SampleCountFlags::TYPE_1, + sample_shading_enable: vk::FALSE, + min_sample_shading: 0.0, + p_sample_mask: ptr::null(), + alpha_to_one_enable: vk::FALSE, + alpha_to_coverage_enable: vk::FALSE, + }; + + let stencil_state = vk::StencilOpState { + fail_op: vk::StencilOp::KEEP, + pass_op: vk::StencilOp::KEEP, + depth_fail_op: vk::StencilOp::KEEP, + compare_op: vk::CompareOp::ALWAYS, + compare_mask: 0, + write_mask: 0, + reference: 0, + }; + + let depth_state_create_info = vk::PipelineDepthStencilStateCreateInfo { + s_type: vk::StructureType::PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineDepthStencilStateCreateFlags::empty(), + depth_test_enable: vk::FALSE, + depth_write_enable: vk::FALSE, + depth_compare_op: vk::CompareOp::LESS_OR_EQUAL, + depth_bounds_test_enable: vk::FALSE, + stencil_test_enable: vk::FALSE, + front: stencil_state, + back: stencil_state, + max_depth_bounds: 1.0, + min_depth_bounds: 0.0, + }; + + let color_blend_attachment_states = [vk::PipelineColorBlendAttachmentState { + blend_enable: vk::FALSE, + color_write_mask: vk::ColorComponentFlags::all(), + src_color_blend_factor: vk::BlendFactor::ONE, + dst_color_blend_factor: vk::BlendFactor::ZERO, + color_blend_op: vk::BlendOp::ADD, + src_alpha_blend_factor: vk::BlendFactor::ONE, + dst_alpha_blend_factor: vk::BlendFactor::ZERO, + alpha_blend_op: vk::BlendOp::ADD, + }]; + + let color_blend_state = vk::PipelineColorBlendStateCreateInfo { + s_type: vk::StructureType::PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineColorBlendStateCreateFlags::empty(), + logic_op_enable: vk::FALSE, + logic_op: vk::LogicOp::COPY, + attachment_count: color_blend_attachment_states.len() as u32, + p_attachments: color_blend_attachment_states.as_ptr(), + blend_constants: [0.0, 0.0, 0.0, 0.0], + }; + + let pipeline_layout_create_info = vk::PipelineLayoutCreateInfo { + s_type: vk::StructureType::PIPELINE_LAYOUT_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineLayoutCreateFlags::empty(), + set_layout_count: 0, + p_set_layouts: ptr::null(), + push_constant_range_count: 0, + p_push_constant_ranges: ptr::null(), + }; + + let pipeline_layout = unsafe { + device + .create_pipeline_layout(&pipeline_layout_create_info, None) + .expect("Failed to create pipeline layout!") + }; + + let graphic_pipeline_create_infos = [vk::GraphicsPipelineCreateInfo { + s_type: vk::StructureType::GRAPHICS_PIPELINE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::PipelineCreateFlags::empty(), + stage_count: shader_stages.len() as u32, + p_stages: shader_stages.as_ptr(), + p_vertex_input_state: &vertex_input_state_create_info, + p_input_assembly_state: &vertex_input_assembly_state_info, + p_tessellation_state: ptr::null(), + p_viewport_state: &viewport_state_create_info, + p_rasterization_state: &rasterization_statue_create_info, + p_multisample_state: &multisample_state_create_info, + p_depth_stencil_state: &depth_state_create_info, + p_color_blend_state: &color_blend_state, + p_dynamic_state: ptr::null(), + layout: pipeline_layout, + render_pass, + subpass: 0, + base_pipeline_handle: vk::Pipeline::null(), + base_pipeline_index: -1, + }]; + + let graphics_pipelines = unsafe { + device + .create_graphics_pipelines( + vk::PipelineCache::null(), + &graphic_pipeline_create_infos, + None, + ) + .expect("Failed to create Graphics Pipeline!.") + }; + + unsafe { + device.destroy_shader_module(vert_shader_module, None); + device.destroy_shader_module(frag_shader_module, None); + } + + (graphics_pipelines[0], pipeline_layout) + } + + pub fn create_framebuffers( + device: &ash::Device, + render_pass: vk::RenderPass, + image_views: &Vec, + swapchain_extent: vk::Extent2D, + ) -> Vec { + let mut framebuffers = vec![]; + + for &image_view in image_views.iter() { + let attachments = [image_view]; + + let framebuffer_create_info = vk::FramebufferCreateInfo { + s_type: vk::StructureType::FRAMEBUFFER_CREATE_INFO, + p_next: ptr::null(), + flags: vk::FramebufferCreateFlags::empty(), + render_pass, + attachment_count: attachments.len() as u32, + p_attachments: attachments.as_ptr(), + width: swapchain_extent.width, + height: swapchain_extent.height, + layers: 1, + }; + + let framebuffer = unsafe { + device + .create_framebuffer(&framebuffer_create_info, None) + .expect("Failed to create Framebuffer!") + }; + + framebuffers.push(framebuffer); + } + + framebuffers + } + + pub fn create_command_pool( + device: &ash::Device, + queue_families: &QueueFamilyIndices, + ) -> vk::CommandPool { + let command_pool_create_info = vk::CommandPoolCreateInfo { + s_type: vk::StructureType::COMMAND_POOL_CREATE_INFO, + p_next: ptr::null(), + flags: vk::CommandPoolCreateFlags::empty(), + queue_family_index: queue_families.graphics_family.unwrap(), + }; + + unsafe { + device + .create_command_pool(&command_pool_create_info, None) + .expect("Failed to create Command Pool!") + } + } + + pub fn create_command_buffers( + device: &ash::Device, + command_pool: vk::CommandPool, + graphics_pipeline: vk::Pipeline, + framebuffers: &Vec, + render_pass: vk::RenderPass, + surface_extent: vk::Extent2D, + ) -> Vec { + let command_buffer_allocate_info = vk::CommandBufferAllocateInfo { + s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, + p_next: ptr::null(), + command_buffer_count: framebuffers.len() as u32, + command_pool, + level: vk::CommandBufferLevel::PRIMARY, + }; + + let command_buffers = unsafe { + device + .allocate_command_buffers(&command_buffer_allocate_info) + .expect("Failed to allocate Command Buffers!") + }; + + for (i, &command_buffer) in command_buffers.iter().enumerate() { + let command_buffer_begin_info = vk::CommandBufferBeginInfo { + s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO, + p_next: ptr::null(), + p_inheritance_info: ptr::null(), + flags: vk::CommandBufferUsageFlags::SIMULTANEOUS_USE, + }; + + unsafe { + device + .begin_command_buffer(command_buffer, &command_buffer_begin_info) + .expect("Failed to begin recording Command Buffer at beginning!"); + } + + let clear_values = [vk::ClearValue { + color: vk::ClearColorValue { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }]; + + let render_pass_begin_info = vk::RenderPassBeginInfo { + s_type: vk::StructureType::RENDER_PASS_BEGIN_INFO, + p_next: ptr::null(), + render_pass, + framebuffer: framebuffers[i], + render_area: vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: surface_extent, + }, + clear_value_count: clear_values.len() as u32, + p_clear_values: clear_values.as_ptr(), + }; + + unsafe { + device.cmd_begin_render_pass( + command_buffer, + &render_pass_begin_info, + vk::SubpassContents::INLINE, + ); + device.cmd_bind_pipeline( + command_buffer, + vk::PipelineBindPoint::GRAPHICS, + graphics_pipeline, + ); + device.cmd_draw(command_buffer, 3, 1, 0, 0); + + device.cmd_end_render_pass(command_buffer); + + device + .end_command_buffer(command_buffer) + .expect("Failed to record Command Buffer at Ending!"); + } + } + + command_buffers + } + + pub fn create_sync_objects(device: &ash::Device, max_frame_in_flight: usize) -> SyncObjects { + let mut sync_objects = SyncObjects { + image_available_semaphores: vec![], + render_finished_semaphores: vec![], + inflight_fences: vec![], + }; + + let semaphore_create_info = vk::SemaphoreCreateInfo { + s_type: vk::StructureType::SEMAPHORE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::SemaphoreCreateFlags::empty(), + }; + + let fence_create_info = vk::FenceCreateInfo { + s_type: vk::StructureType::FENCE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::FenceCreateFlags::SIGNALED, + }; + + for _ in 0..max_frame_in_flight { + unsafe { + let image_available_semaphore = device + .create_semaphore(&semaphore_create_info, None) + .expect("Failed to create Semaphore Object!"); + let render_finished_semaphore = device + .create_semaphore(&semaphore_create_info, None) + .expect("Failed to create Semaphore Object!"); + let inflight_fence = device + .create_fence(&fence_create_info, None) + .expect("Failed to create Fence Object!"); + + sync_objects + .image_available_semaphores + .push(image_available_semaphore); + sync_objects + .render_finished_semaphores + .push(render_finished_semaphore); + sync_objects.inflight_fences.push(inflight_fence); + } + } + + sync_objects + } + + pub fn create_vertex_buffer( + device: &ash::Device, + device_memory_properties: &vk::PhysicalDeviceMemoryProperties, + command_pool: vk::CommandPool, + submit_queue: vk::Queue, + data: &[T], + ) -> (vk::Buffer, vk::DeviceMemory) { + let buffer_size = ::std::mem::size_of_val(data) as vk::DeviceSize; + + let (staging_buffer, staging_buffer_memory) = create_buffer( + device, + buffer_size, + vk::BufferUsageFlags::TRANSFER_SRC, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + &device_memory_properties, + ); + + unsafe { + let data_ptr = device + .map_memory( + staging_buffer_memory, + 0, + buffer_size, + vk::MemoryMapFlags::empty(), + ) + .expect("Failed to Map Memory") as *mut T; + + data_ptr.copy_from_nonoverlapping(data.as_ptr(), data.len()); + + device.unmap_memory(staging_buffer_memory); + } + + let (vertex_buffer, vertex_buffer_memory) = create_buffer( + device, + buffer_size, + vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + &device_memory_properties, + ); + + copy_buffer( + device, + submit_queue, + command_pool, + staging_buffer, + vertex_buffer, + buffer_size, + ); + + unsafe { + device.destroy_buffer(staging_buffer, None); + device.free_memory(staging_buffer_memory, None); + } + + (vertex_buffer, vertex_buffer_memory) + } + + pub fn create_index_buffer( + device: &ash::Device, + device_memory_properties: &vk::PhysicalDeviceMemoryProperties, + command_pool: vk::CommandPool, + submit_queue: vk::Queue, + data: &[u32], + ) -> (vk::Buffer, vk::DeviceMemory) { + let buffer_size = ::std::mem::size_of_val(data) as vk::DeviceSize; + + let (staging_buffer, staging_buffer_memory) = create_buffer( + device, + buffer_size, + vk::BufferUsageFlags::TRANSFER_SRC, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + &device_memory_properties, + ); + + unsafe { + let data_ptr = device + .map_memory( + staging_buffer_memory, + 0, + buffer_size, + vk::MemoryMapFlags::empty(), + ) + .expect("Failed to Map Memory") as *mut u32; + + data_ptr.copy_from_nonoverlapping(data.as_ptr(), data.len()); + + device.unmap_memory(staging_buffer_memory); + } + + let (index_buffer, index_buffer_memory) = create_buffer( + device, + buffer_size, + vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + &device_memory_properties, + ); + + copy_buffer( + device, + submit_queue, + command_pool, + staging_buffer, + index_buffer, + buffer_size, + ); + + unsafe { + device.destroy_buffer(staging_buffer, None); + device.free_memory(staging_buffer_memory, None); + } + + (index_buffer, index_buffer_memory) + } + + pub fn create_descriptor_pool( + device: &ash::Device, + swapchain_images_size: usize, + ) -> vk::DescriptorPool { + let pool_sizes = [vk::DescriptorPoolSize { + ty: vk::DescriptorType::UNIFORM_BUFFER, + descriptor_count: swapchain_images_size as u32, + }]; + + let descriptor_pool_create_info = vk::DescriptorPoolCreateInfo { + s_type: vk::StructureType::DESCRIPTOR_POOL_CREATE_INFO, + p_next: ptr::null(), + flags: vk::DescriptorPoolCreateFlags::empty(), + max_sets: swapchain_images_size as u32, + pool_size_count: pool_sizes.len() as u32, + p_pool_sizes: pool_sizes.as_ptr(), + }; + + unsafe { + device + .create_descriptor_pool(&descriptor_pool_create_info, None) + .expect("Failed to create Descriptor Pool!") + } + } + + pub fn create_descriptor_sets( + device: &ash::Device, + descriptor_pool: vk::DescriptorPool, + descriptor_set_layout: vk::DescriptorSetLayout, + uniforms_buffers: &Vec, + swapchain_images_size: usize, + ) -> Vec { + let mut layouts: Vec = vec![]; + for _ in 0..swapchain_images_size { + layouts.push(descriptor_set_layout); + } + + let descriptor_set_allocate_info = vk::DescriptorSetAllocateInfo { + s_type: vk::StructureType::DESCRIPTOR_SET_ALLOCATE_INFO, + p_next: ptr::null(), + descriptor_pool, + descriptor_set_count: swapchain_images_size as u32, + p_set_layouts: layouts.as_ptr(), + }; + + let descriptor_sets = unsafe { + device + .allocate_descriptor_sets(&descriptor_set_allocate_info) + .expect("Failed to allocate descriptor sets!") + }; + + for (i, &descritptor_set) in descriptor_sets.iter().enumerate() { + let descriptor_buffer_info = [vk::DescriptorBufferInfo { + buffer: uniforms_buffers[i], + offset: 0, + range: ::std::mem::size_of::() as u64, + }]; + + let descriptor_write_sets = [vk::WriteDescriptorSet { + s_type: vk::StructureType::WRITE_DESCRIPTOR_SET, + p_next: ptr::null(), + dst_set: descritptor_set, + dst_binding: 0, + dst_array_element: 0, + descriptor_count: 1, + descriptor_type: vk::DescriptorType::UNIFORM_BUFFER, + p_image_info: ptr::null(), + p_buffer_info: descriptor_buffer_info.as_ptr(), + p_texel_buffer_view: ptr::null(), + }]; + + unsafe { + device.update_descriptor_sets(&descriptor_write_sets, &[]); + } + } + + descriptor_sets + } + + pub fn create_descriptor_set_layout(device: &ash::Device) -> vk::DescriptorSetLayout { + let ubo_layout_bindings = [vk::DescriptorSetLayoutBinding { + binding: 0, + descriptor_type: vk::DescriptorType::UNIFORM_BUFFER, + descriptor_count: 1, + stage_flags: vk::ShaderStageFlags::VERTEX, + p_immutable_samplers: ptr::null(), + }]; + + let ubo_layout_create_info = vk::DescriptorSetLayoutCreateInfo { + s_type: vk::StructureType::DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + p_next: ptr::null(), + flags: vk::DescriptorSetLayoutCreateFlags::empty(), + binding_count: ubo_layout_bindings.len() as u32, + p_bindings: ubo_layout_bindings.as_ptr(), + }; + + unsafe { + device + .create_descriptor_set_layout(&ubo_layout_create_info, None) + .expect("Failed to create Descriptor Set Layout!") + } + } + + pub fn create_uniform_buffers( + device: &ash::Device, + device_memory_properties: &vk::PhysicalDeviceMemoryProperties, + swapchain_image_count: usize, + ) -> (Vec, Vec) { + let buffer_size = ::std::mem::size_of::(); + + let mut uniform_buffers = vec![]; + let mut uniform_buffers_memory = vec![]; + + for _ in 0..swapchain_image_count { + let (uniform_buffer, uniform_buffer_memory) = create_buffer( + device, + buffer_size as u64, + vk::BufferUsageFlags::UNIFORM_BUFFER, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + device_memory_properties, + ); + uniform_buffers.push(uniform_buffer); + uniform_buffers_memory.push(uniform_buffer_memory); + } + + (uniform_buffers, uniform_buffers_memory) + } + + pub fn create_image( + device: &ash::Device, + width: u32, + height: u32, + mip_levels: u32, + num_samples: vk::SampleCountFlags, + format: vk::Format, + tiling: vk::ImageTiling, + usage: vk::ImageUsageFlags, + required_memory_properties: vk::MemoryPropertyFlags, + device_memory_properties: &vk::PhysicalDeviceMemoryProperties, + ) -> (vk::Image, vk::DeviceMemory) { + let image_create_info = vk::ImageCreateInfo { + s_type: vk::StructureType::IMAGE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::ImageCreateFlags::empty(), + image_type: vk::ImageType::TYPE_2D, + format, + mip_levels, + array_layers: 1, + samples: num_samples, + tiling, + usage, + sharing_mode: vk::SharingMode::EXCLUSIVE, + queue_family_index_count: 0, + p_queue_family_indices: ptr::null(), + initial_layout: vk::ImageLayout::UNDEFINED, + extent: vk::Extent3D { + width, + height, + depth: 1, + }, + }; + + let texture_image = unsafe { + device + .create_image(&image_create_info, None) + .expect("Failed to create Texture Image!") + }; + + let image_memory_requirement = unsafe { device.get_image_memory_requirements(texture_image) }; + let memory_allocate_info = vk::MemoryAllocateInfo { + s_type: vk::StructureType::MEMORY_ALLOCATE_INFO, + p_next: ptr::null(), + allocation_size: image_memory_requirement.size, + memory_type_index: find_memory_type( + image_memory_requirement.memory_type_bits, + required_memory_properties, + device_memory_properties, + ), + }; + + let texture_image_memory = unsafe { + device + .allocate_memory(&memory_allocate_info, None) + .expect("Failed to allocate Texture Image memory!") + }; + + unsafe { + device + .bind_image_memory(texture_image, texture_image_memory, 0) + .expect("Failed to bind Image Memmory!"); + } + + (texture_image, texture_image_memory) + } + + pub fn transition_image_layout( + device: &ash::Device, + command_pool: vk::CommandPool, + submit_queue: vk::Queue, + image: vk::Image, + _format: vk::Format, + old_layout: vk::ImageLayout, + new_layout: vk::ImageLayout, + mip_levels: u32, + ) { + let command_buffer = begin_single_time_command(device, command_pool); + + let src_access_mask; + let dst_access_mask; + let source_stage; + let destination_stage; + + if old_layout == vk::ImageLayout::UNDEFINED + && new_layout == vk::ImageLayout::TRANSFER_DST_OPTIMAL + { + src_access_mask = vk::AccessFlags::empty(); + dst_access_mask = vk::AccessFlags::TRANSFER_WRITE; + source_stage = vk::PipelineStageFlags::TOP_OF_PIPE; + destination_stage = vk::PipelineStageFlags::TRANSFER; + } else if old_layout == vk::ImageLayout::TRANSFER_DST_OPTIMAL + && new_layout == vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL + { + src_access_mask = vk::AccessFlags::TRANSFER_WRITE; + dst_access_mask = vk::AccessFlags::SHADER_READ; + source_stage = vk::PipelineStageFlags::TRANSFER; + destination_stage = vk::PipelineStageFlags::FRAGMENT_SHADER; + } else if old_layout == vk::ImageLayout::UNDEFINED + && new_layout == vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL + { + src_access_mask = vk::AccessFlags::empty(); + dst_access_mask = + vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE; + source_stage = vk::PipelineStageFlags::TOP_OF_PIPE; + destination_stage = vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT; + } else { + panic!("Unsupported layout transition!") + } + + let image_barriers = [vk::ImageMemoryBarrier { + s_type: vk::StructureType::IMAGE_MEMORY_BARRIER, + p_next: ptr::null(), + src_access_mask, + dst_access_mask, + old_layout, + new_layout, + src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + image, + subresource_range: vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: mip_levels, + base_array_layer: 0, + layer_count: 1, + }, + }]; + + unsafe { + device.cmd_pipeline_barrier( + command_buffer, + source_stage, + destination_stage, + vk::DependencyFlags::empty(), + &[], + &[], + &image_barriers, + ); + } + + end_single_time_command(device, command_pool, submit_queue, command_buffer); + } + + pub fn create_image_views( + device: &ash::Device, + surface_format: vk::Format, + images: &Vec, + ) -> Vec { + let swapchain_imageviews: Vec = images + .iter() + .map(|&image| { + create_image_view( + device, + image, + surface_format, + vk::ImageAspectFlags::COLOR, + 1, + ) + }) + .collect(); + + swapchain_imageviews + } + + pub fn create_image_view( + device: &ash::Device, + image: vk::Image, + format: vk::Format, + aspect_flags: vk::ImageAspectFlags, + mip_levels: u32, + ) -> vk::ImageView { + let imageview_create_info = vk::ImageViewCreateInfo { + s_type: vk::StructureType::IMAGE_VIEW_CREATE_INFO, + p_next: ptr::null(), + flags: vk::ImageViewCreateFlags::empty(), + view_type: vk::ImageViewType::TYPE_2D, + format, + components: vk::ComponentMapping { + r: vk::ComponentSwizzle::IDENTITY, + g: vk::ComponentSwizzle::IDENTITY, + b: vk::ComponentSwizzle::IDENTITY, + a: vk::ComponentSwizzle::IDENTITY, + }, + subresource_range: vk::ImageSubresourceRange { + aspect_mask: aspect_flags, + base_mip_level: 0, + level_count: mip_levels, + base_array_layer: 0, + layer_count: 1, + }, + image, + }; + + unsafe { + device + .create_image_view(&imageview_create_info, None) + .expect("Failed to create Image View!") + } + } + + pub fn create_texture_image_view( + device: &ash::Device, + texture_image: vk::Image, + mip_levels: u32, + ) -> vk::ImageView { + create_image_view( + device, + texture_image, + vk::Format::R8G8B8A8_SRGB, + vk::ImageAspectFlags::COLOR, + mip_levels, + ) + } + + pub fn create_texture_sampler(device: &ash::Device) -> vk::Sampler { + let sampler_create_info = vk::SamplerCreateInfo { + s_type: vk::StructureType::SAMPLER_CREATE_INFO, + p_next: ptr::null(), + flags: vk::SamplerCreateFlags::empty(), + mag_filter: vk::Filter::LINEAR, + min_filter: vk::Filter::LINEAR, + address_mode_u: vk::SamplerAddressMode::REPEAT, + address_mode_v: vk::SamplerAddressMode::REPEAT, + address_mode_w: vk::SamplerAddressMode::REPEAT, + max_anisotropy: 16.0, + compare_enable: vk::FALSE, + compare_op: vk::CompareOp::ALWAYS, + mipmap_mode: vk::SamplerMipmapMode::LINEAR, + min_lod: 0.0, + max_lod: 0.0, + mip_lod_bias: 0.0, + border_color: vk::BorderColor::INT_OPAQUE_BLACK, + anisotropy_enable: vk::TRUE, + unnormalized_coordinates: vk::FALSE, + }; + + unsafe { + device + .create_sampler(&sampler_create_info, None) + .expect("Failed to create Sampler!") + } + } + + pub fn create_texture_image( + device: &ash::Device, + command_pool: vk::CommandPool, + submit_queue: vk::Queue, + device_memory_properties: &vk::PhysicalDeviceMemoryProperties, + image_path: &Path, + ) -> (vk::Image, vk::DeviceMemory) { + let mut image_object = image::open(image_path).unwrap(); // this function is slow in debug mode. + image_object = image_object.flipv(); + let (image_width, image_height) = (image_object.width(), image_object.height()); + let image_data = match &image_object { + image::DynamicImage::ImageBgr8(_) + | image::DynamicImage::ImageLuma8(_) + | image::DynamicImage::ImageRgb8(_) => image_object.to_rgba().into_raw(), + image::DynamicImage::ImageBgra8(_) + | image::DynamicImage::ImageLumaA8(_) + | image::DynamicImage::ImageRgba8(_) => image_object.raw_pixels(), + }; + let image_size = + (::std::mem::size_of::() as u32 * image_width * image_height * 4) as vk::DeviceSize; + + if image_size <= 0 { + panic!("Failed to load texture image!") + } + + let (staging_buffer, staging_buffer_memory) = create_buffer( + device, + image_size, + vk::BufferUsageFlags::TRANSFER_SRC, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + device_memory_properties, + ); + + unsafe { + let data_ptr = device + .map_memory( + staging_buffer_memory, + 0, + image_size, + vk::MemoryMapFlags::empty(), + ) + .expect("Failed to Map Memory") as *mut u8; + + data_ptr.copy_from_nonoverlapping(image_data.as_ptr(), image_data.len()); + + device.unmap_memory(staging_buffer_memory); + } + + let (texture_image, texture_image_memory) = create_image( + device, + image_width, + image_height, + 1, + vk::SampleCountFlags::TYPE_1, + vk::Format::R8G8B8A8_SRGB, + vk::ImageTiling::OPTIMAL, + vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + device_memory_properties, + ); + + transition_image_layout( + device, + command_pool, + submit_queue, + texture_image, + vk::Format::R8G8B8A8_SRGB, + vk::ImageLayout::UNDEFINED, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + 1, + ); + + copy_buffer_to_image( + device, + command_pool, + submit_queue, + staging_buffer, + texture_image, + image_width, + image_height, + ); + + transition_image_layout( + device, + command_pool, + submit_queue, + texture_image, + vk::Format::R8G8B8A8_UNORM, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + 1, + ); + + unsafe { + device.destroy_buffer(staging_buffer, None); + device.free_memory(staging_buffer_memory, None); + } + + (texture_image, texture_image_memory) + } + + pub fn create_depth_resources( + instance: &ash::Instance, + device: &ash::Device, + physical_device: vk::PhysicalDevice, + _command_pool: vk::CommandPool, + _submit_queue: vk::Queue, + swapchain_extent: vk::Extent2D, + device_memory_properties: &vk::PhysicalDeviceMemoryProperties, + msaa_samples: vk::SampleCountFlags, + ) -> (vk::Image, vk::ImageView, vk::DeviceMemory) { + let depth_format = find_depth_format(instance, physical_device); + let (depth_image, depth_image_memory) = create_image( + device, + swapchain_extent.width, + swapchain_extent.height, + 1, + msaa_samples, + depth_format, + vk::ImageTiling::OPTIMAL, + vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + device_memory_properties, + ); + let depth_image_view = create_image_view( + device, + depth_image, + depth_format, + vk::ImageAspectFlags::DEPTH, + 1, + ); + + (depth_image, depth_image_view, depth_image_memory) + } + + pub fn generate_mipmaps( + device: &ash::Device, + command_pool: vk::CommandPool, + submit_queue: vk::Queue, + image: vk::Image, + tex_width: u32, + tex_height: u32, + mip_levels: u32, + ) { + let command_buffer = begin_single_time_command(device, command_pool); + + let mut image_barrier = vk::ImageMemoryBarrier { + s_type: vk::StructureType::IMAGE_MEMORY_BARRIER, + p_next: ptr::null(), + src_access_mask: vk::AccessFlags::empty(), + dst_access_mask: vk::AccessFlags::empty(), + old_layout: vk::ImageLayout::UNDEFINED, + new_layout: vk::ImageLayout::UNDEFINED, + src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + image, + subresource_range: vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }, + }; + + let mut mip_width = tex_width as i32; + let mut mip_height = tex_height as i32; + + for i in 1..mip_levels { + image_barrier.subresource_range.base_mip_level = i - 1; + image_barrier.old_layout = vk::ImageLayout::TRANSFER_DST_OPTIMAL; + image_barrier.new_layout = vk::ImageLayout::TRANSFER_SRC_OPTIMAL; + image_barrier.src_access_mask = vk::AccessFlags::TRANSFER_WRITE; + image_barrier.dst_access_mask = vk::AccessFlags::TRANSFER_READ; + + unsafe { + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::TRANSFER, + vk::DependencyFlags::empty(), + &[], + &[], + &[image_barrier.clone()], + ); + } + + let blits = [vk::ImageBlit { + src_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: i - 1, + base_array_layer: 0, + layer_count: 1, + }, + src_offsets: [ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: mip_width, + y: mip_height, + z: 1, + }, + ], + dst_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: i, + base_array_layer: 0, + layer_count: 1, + }, + dst_offsets: [ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: max(mip_width / 2, 1), + y: max(mip_height / 2, 1), + z: 1, + }, + ], + }]; + + unsafe { + device.cmd_blit_image( + command_buffer, + image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &blits, + vk::Filter::LINEAR, + ); + } + + image_barrier.old_layout = vk::ImageLayout::TRANSFER_SRC_OPTIMAL; + image_barrier.new_layout = vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL; + image_barrier.src_access_mask = vk::AccessFlags::TRANSFER_READ; + image_barrier.dst_access_mask = vk::AccessFlags::SHADER_READ; + + unsafe { + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::DependencyFlags::empty(), + &[], + &[], + &[image_barrier.clone()], + ); + } + + mip_width = max(mip_width / 2, 1); + mip_height = max(mip_height / 2, 1); + } + + image_barrier.subresource_range.base_mip_level = mip_levels - 1; + image_barrier.old_layout = vk::ImageLayout::TRANSFER_DST_OPTIMAL; + image_barrier.new_layout = vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL; + image_barrier.src_access_mask = vk::AccessFlags::TRANSFER_WRITE; + image_barrier.dst_access_mask = vk::AccessFlags::SHADER_READ; + + unsafe { + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::DependencyFlags::empty(), + &[], + &[], + &[image_barrier.clone()], + ); + } + + end_single_time_command(device, command_pool, submit_queue, command_buffer); + } +} + +pub mod v2 { + use ash::vk; + + use std::ptr; + + use super::*; + + pub fn create_descriptor_pool( + device: &ash::Device, + swapchain_images_size: usize, + ) -> vk::DescriptorPool { + let pool_sizes = [ + vk::DescriptorPoolSize { + // transform descriptor pool + ty: vk::DescriptorType::UNIFORM_BUFFER, + descriptor_count: swapchain_images_size as u32, + }, + vk::DescriptorPoolSize { + // sampler descriptor pool + ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, + descriptor_count: swapchain_images_size as u32, + }, + ]; + + let descriptor_pool_create_info = vk::DescriptorPoolCreateInfo { + s_type: vk::StructureType::DESCRIPTOR_POOL_CREATE_INFO, + p_next: ptr::null(), + flags: vk::DescriptorPoolCreateFlags::empty(), + max_sets: swapchain_images_size as u32, + pool_size_count: pool_sizes.len() as u32, + p_pool_sizes: pool_sizes.as_ptr(), + }; + + unsafe { + device + .create_descriptor_pool(&descriptor_pool_create_info, None) + .expect("Failed to create Descriptor Pool!") + } + } + + pub fn create_descriptor_sets( + device: &ash::Device, + descriptor_pool: vk::DescriptorPool, + descriptor_set_layout: vk::DescriptorSetLayout, + uniforms_buffers: &Vec, + texture_image_view: vk::ImageView, + texture_sampler: vk::Sampler, + swapchain_images_size: usize, + ) -> Vec { + let mut layouts: Vec = vec![]; + for _ in 0..swapchain_images_size { + layouts.push(descriptor_set_layout); + } + + let descriptor_set_allocate_info = vk::DescriptorSetAllocateInfo { + s_type: vk::StructureType::DESCRIPTOR_SET_ALLOCATE_INFO, + p_next: ptr::null(), + descriptor_pool, + descriptor_set_count: swapchain_images_size as u32, + p_set_layouts: layouts.as_ptr(), + }; + + let descriptor_sets = unsafe { + device + .allocate_descriptor_sets(&descriptor_set_allocate_info) + .expect("Failed to allocate descriptor sets!") + }; + + for (i, &descritptor_set) in descriptor_sets.iter().enumerate() { + let descriptor_buffer_infos = [vk::DescriptorBufferInfo { + buffer: uniforms_buffers[i], + offset: 0, + range: ::std::mem::size_of::() as u64, + }]; + + let descriptor_image_infos = [vk::DescriptorImageInfo { + sampler: texture_sampler, + image_view: texture_image_view, + image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + }]; + + let descriptor_write_sets = [ + vk::WriteDescriptorSet { + // transform uniform + s_type: vk::StructureType::WRITE_DESCRIPTOR_SET, + p_next: ptr::null(), + dst_set: descritptor_set, + dst_binding: 0, + dst_array_element: 0, + descriptor_count: 1, + descriptor_type: vk::DescriptorType::UNIFORM_BUFFER, + p_image_info: ptr::null(), + p_buffer_info: descriptor_buffer_infos.as_ptr(), + p_texel_buffer_view: ptr::null(), + }, + vk::WriteDescriptorSet { + // sampler uniform + s_type: vk::StructureType::WRITE_DESCRIPTOR_SET, + p_next: ptr::null(), + dst_set: descritptor_set, + dst_binding: 1, + dst_array_element: 0, + descriptor_count: 1, + descriptor_type: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, + p_image_info: descriptor_image_infos.as_ptr(), + p_buffer_info: ptr::null(), + p_texel_buffer_view: ptr::null(), + }, + ]; + + unsafe { + device.update_descriptor_sets(&descriptor_write_sets, &[]); + } + } + + descriptor_sets + } + + pub fn create_descriptor_set_layout(device: &ash::Device) -> vk::DescriptorSetLayout { + let ubo_layout_bindings = [ + vk::DescriptorSetLayoutBinding { + // transform uniform + binding: 0, + descriptor_type: vk::DescriptorType::UNIFORM_BUFFER, + descriptor_count: 1, + stage_flags: vk::ShaderStageFlags::VERTEX, + p_immutable_samplers: ptr::null(), + }, + vk::DescriptorSetLayoutBinding { + // sampler uniform + binding: 1, + descriptor_type: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, + descriptor_count: 1, + stage_flags: vk::ShaderStageFlags::FRAGMENT, + p_immutable_samplers: ptr::null(), + }, + ]; + + let ubo_layout_create_info = vk::DescriptorSetLayoutCreateInfo { + s_type: vk::StructureType::DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + p_next: ptr::null(), + flags: vk::DescriptorSetLayoutCreateFlags::empty(), + binding_count: ubo_layout_bindings.len() as u32, + p_bindings: ubo_layout_bindings.as_ptr(), + }; + + unsafe { + device + .create_descriptor_set_layout(&ubo_layout_create_info, None) + .expect("Failed to create Descriptor Set Layout!") + } + } +} + +use ash::version::DeviceV1_0; +use ash::version::EntryV1_0; +use ash::version::InstanceV1_0; +use ash::vk; + +use std::ffi::CString; +use std::os::raw::c_char; +use std::os::raw::c_void; +use std::path::Path; +use std::ptr; + + +use crate::utility::constants::*; +use crate::utility::debug; +use crate::utility::platforms; +use crate::utility::structures::*; + +pub fn create_instance( + entry: &ash::Entry, + window_title: &str, + is_enable_debug: bool, + required_validation_layers: &Vec<&str>, +) -> ash::Instance { + if is_enable_debug + && debug::check_validation_layer_support(entry, required_validation_layers) == false + { + panic!("Validation layers requested, but not available!"); + } + + let app_name = CString::new(window_title).unwrap(); + let engine_name = CString::new("Vulkan Engine").unwrap(); + let app_info = vk::ApplicationInfo { + p_application_name: app_name.as_ptr(), + s_type: vk::StructureType::APPLICATION_INFO, + p_next: ptr::null(), + application_version: APPLICATION_VERSION, + p_engine_name: engine_name.as_ptr(), + engine_version: ENGINE_VERSION, + api_version: API_VERSION, + }; + + // This create info used to debug issues in vk::createInstance and vk::destroyInstance. + let debug_utils_create_info = debug::populate_debug_messenger_create_info(); + + // VK_EXT debug report has been requested here. + let extension_names = platforms::required_extension_names(); + + let requred_validation_layer_raw_names: Vec = required_validation_layers + .iter() + .map(|layer_name| CString::new(*layer_name).unwrap()) + .collect(); + let layer_names: Vec<*const i8> = requred_validation_layer_raw_names + .iter() + .map(|layer_name| layer_name.as_ptr()) + .collect(); + + let create_info = vk::InstanceCreateInfo { + s_type: vk::StructureType::INSTANCE_CREATE_INFO, + p_next: if VALIDATION.is_enable { + &debug_utils_create_info as *const vk::DebugUtilsMessengerCreateInfoEXT + as *const c_void + } else { + ptr::null() + }, + flags: vk::InstanceCreateFlags::empty(), + p_application_info: &app_info, + pp_enabled_layer_names: if is_enable_debug { + layer_names.as_ptr() + } else { + ptr::null() + }, + enabled_layer_count: if is_enable_debug { + layer_names.len() + } else { + 0 + } as u32, + pp_enabled_extension_names: extension_names.as_ptr(), + enabled_extension_count: extension_names.len() as u32, + }; + + let instance: ash::Instance = unsafe { + entry + .create_instance(&create_info, None) + .expect("Failed to create instance!") + }; + + instance +} + +pub fn create_surface( + entry: &ash::Entry, + instance: &ash::Instance, + window: &winit::window::Window, + screen_width: u32, + screen_height: u32, +) -> SurfaceStuff { + let surface = unsafe { + platforms::create_surface(entry, instance, window).expect("Failed to create surface.") + }; + let surface_loader = ash::extensions::khr::Surface::new(entry, instance); + + SurfaceStuff { + surface_loader, + surface, + screen_width, + screen_height, + } +} + +pub fn pick_physical_device( + instance: &ash::Instance, + surface_stuff: &SurfaceStuff, + required_device_extensions: &DeviceExtension, +) -> vk::PhysicalDevice { + let physical_devices = unsafe { + instance + .enumerate_physical_devices() + .expect("Failed to enumerate Physical Devices!") + }; + + let result = physical_devices.iter().find(|physical_device| { + let is_suitable = is_physical_device_suitable( + instance, + **physical_device, + surface_stuff, + required_device_extensions, + ); + + // if is_suitable { + // let device_properties = instance.get_physical_device_properties(**physical_device); + // let device_name = super::tools::vk_to_string(&device_properties.device_name); + // println!("Using GPU: {}", device_name); + // } + + is_suitable + }); + + match result { + Some(p_physical_device) => *p_physical_device, + None => panic!("Failed to find a suitable GPU!"), + } +} + +pub fn is_physical_device_suitable( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, + surface_stuff: &SurfaceStuff, + required_device_extensions: &DeviceExtension, +) -> bool { + let device_features = unsafe { instance.get_physical_device_features(physical_device) }; + + let indices = find_queue_family(instance, physical_device, surface_stuff); + + let is_queue_family_supported = indices.is_complete(); + let is_device_extension_supported = + check_device_extension_support(instance, physical_device, required_device_extensions); + let is_swapchain_supported = if is_device_extension_supported { + let swapchain_support = query_swapchain_support(physical_device, surface_stuff); + !swapchain_support.formats.is_empty() && !swapchain_support.present_modes.is_empty() + } else { + false + }; + let is_support_sampler_anisotropy = device_features.sampler_anisotropy == 1; + + return is_queue_family_supported + && is_device_extension_supported + && is_swapchain_supported + && is_support_sampler_anisotropy; +} + +pub fn create_logical_device( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, + validation: &super::debug::ValidationInfo, + device_extensions: &DeviceExtension, + surface_stuff: &SurfaceStuff, +) -> (ash::Device, QueueFamilyIndices) { + let indices = find_queue_family(instance, physical_device, surface_stuff); + + use std::collections::HashSet; + let mut unique_queue_families = HashSet::new(); + unique_queue_families.insert(indices.graphics_family.unwrap()); + unique_queue_families.insert(indices.present_family.unwrap()); + + let queue_priorities = [1.0_f32]; + let mut queue_create_infos = vec![]; + for &queue_family in unique_queue_families.iter() { + let queue_create_info = vk::DeviceQueueCreateInfo { + s_type: vk::StructureType::DEVICE_QUEUE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::DeviceQueueCreateFlags::empty(), + queue_family_index: queue_family, + p_queue_priorities: queue_priorities.as_ptr(), + queue_count: queue_priorities.len() as u32, + }; + queue_create_infos.push(queue_create_info); + } + + let physical_device_features = vk::PhysicalDeviceFeatures { + sampler_anisotropy: vk::TRUE, // enable anisotropy device feature from Chapter-24. + ..Default::default() + }; + + let requred_validation_layer_raw_names: Vec = validation + .required_validation_layers + .iter() + .map(|layer_name| CString::new(*layer_name).unwrap()) + .collect(); + let enable_layer_names: Vec<*const c_char> = requred_validation_layer_raw_names + .iter() + .map(|layer_name| layer_name.as_ptr()) + .collect(); + + let enable_extension_names = device_extensions.get_extensions_raw_names(); + + let device_create_info = vk::DeviceCreateInfo { + s_type: vk::StructureType::DEVICE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::DeviceCreateFlags::empty(), + queue_create_info_count: queue_create_infos.len() as u32, + p_queue_create_infos: queue_create_infos.as_ptr(), + enabled_layer_count: if validation.is_enable { + enable_layer_names.len() + } else { + 0 + } as u32, + pp_enabled_layer_names: if validation.is_enable { + enable_layer_names.as_ptr() + } else { + ptr::null() + }, + enabled_extension_count: enable_extension_names.len() as u32, + pp_enabled_extension_names: enable_extension_names.as_ptr(), + p_enabled_features: &physical_device_features, + }; + + let device: ash::Device = unsafe { + instance + .create_device(physical_device, &device_create_info, None) + .expect("Failed to create logical Device!") + }; + + (device, indices) +} + +pub fn find_queue_family( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, + surface_stuff: &SurfaceStuff, +) -> QueueFamilyIndices { + let queue_families = + unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; + + let mut queue_family_indices = QueueFamilyIndices::new(); + + let mut index = 0; + for queue_family in queue_families.iter() { + if queue_family.queue_count > 0 + && queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) + { + queue_family_indices.graphics_family = Some(index); + } + + let is_present_support = unsafe { + surface_stuff + .surface_loader + .get_physical_device_surface_support( + physical_device, + index as u32, + surface_stuff.surface, + ) + }; + if queue_family.queue_count > 0 && is_present_support { + queue_family_indices.present_family = Some(index); + } + + if queue_family_indices.is_complete() { + break; + } + + index += 1; + } + + queue_family_indices +} + +pub fn check_device_extension_support( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, + device_extensions: &DeviceExtension, +) -> bool { + let available_extensions = unsafe { + instance + .enumerate_device_extension_properties(physical_device) + .expect("Failed to get device extension properties.") + }; + + let mut available_extension_names = vec![]; + + for extension in available_extensions.iter() { + let extension_name = super::tools::vk_to_string(&extension.extension_name); + + available_extension_names.push(extension_name); + } + + use std::collections::HashSet; + let mut required_extensions = HashSet::new(); + for extension in device_extensions.names.iter() { + required_extensions.insert(extension.to_string()); + } + + for extension_name in available_extension_names.iter() { + required_extensions.remove(extension_name); + } + + return required_extensions.is_empty(); +} + +pub fn query_swapchain_support( + physical_device: vk::PhysicalDevice, + surface_stuff: &SurfaceStuff, +) -> SwapChainSupportDetail { + unsafe { + let capabilities = surface_stuff + .surface_loader + .get_physical_device_surface_capabilities(physical_device, surface_stuff.surface) + .expect("Failed to query for surface capabilities."); + let formats = surface_stuff + .surface_loader + .get_physical_device_surface_formats(physical_device, surface_stuff.surface) + .expect("Failed to query for surface formats."); + let present_modes = surface_stuff + .surface_loader + .get_physical_device_surface_present_modes(physical_device, surface_stuff.surface) + .expect("Failed to query for surface present mode."); + + SwapChainSupportDetail { + capabilities, + formats, + present_modes, + } + } +} + +pub fn create_swapchain( + instance: &ash::Instance, + device: &ash::Device, + physical_device: vk::PhysicalDevice, + window: &winit::window::Window, + surface_stuff: &SurfaceStuff, + queue_family: &QueueFamilyIndices, +) -> SwapChainStuff { + let swapchain_support = query_swapchain_support(physical_device, surface_stuff); + + let surface_format = choose_swapchain_format(&swapchain_support.formats); + let present_mode = choose_swapchain_present_mode(&swapchain_support.present_modes); + let extent = choose_swapchain_extent(&swapchain_support.capabilities, window); + + let image_count = swapchain_support.capabilities.min_image_count + 1; + let image_count = if swapchain_support.capabilities.max_image_count > 0 { + image_count.min(swapchain_support.capabilities.max_image_count) + } else { + image_count + }; + + let (image_sharing_mode, queue_family_index_count, queue_family_indices) = + if queue_family.graphics_family != queue_family.present_family { + ( + vk::SharingMode::CONCURRENT, + 2, + vec![ + queue_family.graphics_family.unwrap(), + queue_family.present_family.unwrap(), + ], + ) + } else { + (vk::SharingMode::EXCLUSIVE, 0, vec![]) + }; + + let swapchain_create_info = vk::SwapchainCreateInfoKHR { + s_type: vk::StructureType::SWAPCHAIN_CREATE_INFO_KHR, + p_next: ptr::null(), + flags: vk::SwapchainCreateFlagsKHR::empty(), + surface: surface_stuff.surface, + min_image_count: image_count, + image_color_space: surface_format.color_space, + image_format: surface_format.format, + image_extent: extent, + image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT, + image_sharing_mode, + p_queue_family_indices: queue_family_indices.as_ptr(), + queue_family_index_count, + pre_transform: swapchain_support.capabilities.current_transform, + composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE, + present_mode, + clipped: vk::TRUE, + old_swapchain: vk::SwapchainKHR::null(), + image_array_layers: 1, + }; + + let swapchain_loader = ash::extensions::khr::Swapchain::new(instance, device); + let swapchain = unsafe { + swapchain_loader + .create_swapchain(&swapchain_create_info, None) + .expect("Failed to create Swapchain!") + }; + + let swapchain_images = unsafe { + swapchain_loader + .get_swapchain_images(swapchain) + .expect("Failed to get Swapchain Images.") + }; + + SwapChainStuff { + swapchain_loader, + swapchain, + swapchain_format: surface_format.format, + swapchain_extent: extent, + swapchain_images, + } +} + +pub fn choose_swapchain_format( + available_formats: &Vec, +) -> vk::SurfaceFormatKHR { + + for available_format in available_formats { + if available_format.format == vk::Format::B8G8R8A8_SRGB + && available_format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR + { + return available_format.clone(); + } + } + + return available_formats.first().unwrap().clone(); +} + +pub fn choose_swapchain_present_mode( + available_present_modes: &Vec, +) -> vk::PresentModeKHR { + for &available_present_mode in available_present_modes.iter() { + if available_present_mode == vk::PresentModeKHR::MAILBOX { + return available_present_mode; + } + } + + vk::PresentModeKHR::FIFO +} + +pub fn choose_swapchain_extent( + capabilities: &vk::SurfaceCapabilitiesKHR, + window: &winit::window::Window, +) -> vk::Extent2D { + if capabilities.current_extent.width != u32::max_value() { + capabilities.current_extent + } else { + use num::clamp; + + let window_size = window + .inner_size(); + println!( + "\t\tInner Window Size: ({}, {})", + window_size.width, window_size.height + ); + + vk::Extent2D { + width: clamp( + window_size.width as u32, + capabilities.min_image_extent.width, + capabilities.max_image_extent.width, + ), + height: clamp( + window_size.height as u32, + capabilities.min_image_extent.height, + capabilities.max_image_extent.height, + ), + } + } +} + +pub fn create_shader_module(device: &ash::Device, code: Vec) -> vk::ShaderModule { + let shader_module_create_info = vk::ShaderModuleCreateInfo { + s_type: vk::StructureType::SHADER_MODULE_CREATE_INFO, + p_next: ptr::null(), + flags: vk::ShaderModuleCreateFlags::empty(), + code_size: code.len(), + p_code: code.as_ptr() as *const u32, + }; + + unsafe { + device + .create_shader_module(&shader_module_create_info, None) + .expect("Failed to create Shader Module!") + } +} + +pub fn create_buffer( + device: &ash::Device, + size: vk::DeviceSize, + usage: vk::BufferUsageFlags, + required_memory_properties: vk::MemoryPropertyFlags, + device_memory_properties: &vk::PhysicalDeviceMemoryProperties, +) -> (vk::Buffer, vk::DeviceMemory) { + let buffer_create_info = vk::BufferCreateInfo { + s_type: vk::StructureType::BUFFER_CREATE_INFO, + p_next: ptr::null(), + flags: vk::BufferCreateFlags::empty(), + size, + usage, + sharing_mode: vk::SharingMode::EXCLUSIVE, + queue_family_index_count: 0, + p_queue_family_indices: ptr::null(), + }; + + let buffer = unsafe { + device + .create_buffer(&buffer_create_info, None) + .expect("Failed to create Vertex Buffer") + }; + + let mem_requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; + let memory_type = find_memory_type( + mem_requirements.memory_type_bits, + required_memory_properties, + device_memory_properties, + ); + + let allocate_info = vk::MemoryAllocateInfo { + s_type: vk::StructureType::MEMORY_ALLOCATE_INFO, + p_next: ptr::null(), + allocation_size: mem_requirements.size, + memory_type_index: memory_type, + }; + + let buffer_memory = unsafe { + device + .allocate_memory(&allocate_info, None) + .expect("Failed to allocate vertex buffer memory!") + }; + + unsafe { + device + .bind_buffer_memory(buffer, buffer_memory, 0) + .expect("Failed to bind Buffer"); + } + + (buffer, buffer_memory) +} + +pub fn copy_buffer( + device: &ash::Device, + submit_queue: vk::Queue, + command_pool: vk::CommandPool, + src_buffer: vk::Buffer, + dst_buffer: vk::Buffer, + size: vk::DeviceSize, +) { + let command_buffer = begin_single_time_command(device, command_pool); + + let copy_regions = [vk::BufferCopy { + src_offset: 0, + dst_offset: 0, + size, + }]; + + unsafe { + device.cmd_copy_buffer(command_buffer, src_buffer, dst_buffer, ©_regions); + } + + end_single_time_command(device, command_pool, submit_queue, command_buffer); +} + +pub fn begin_single_time_command( + device: &ash::Device, + command_pool: vk::CommandPool, +) -> vk::CommandBuffer { + let command_buffer_allocate_info = vk::CommandBufferAllocateInfo { + s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, + p_next: ptr::null(), + command_buffer_count: 1, + command_pool, + level: vk::CommandBufferLevel::PRIMARY, + }; + + let command_buffer = unsafe { + device + .allocate_command_buffers(&command_buffer_allocate_info) + .expect("Failed to allocate Command Buffers!") + }[0]; + + let command_buffer_begin_info = vk::CommandBufferBeginInfo { + s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO, + p_next: ptr::null(), + p_inheritance_info: ptr::null(), + flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT, + }; + + unsafe { + device + .begin_command_buffer(command_buffer, &command_buffer_begin_info) + .expect("Failed to begin recording Command Buffer at beginning!"); + } + + command_buffer +} + +pub fn end_single_time_command( + device: &ash::Device, + command_pool: vk::CommandPool, + submit_queue: vk::Queue, + command_buffer: vk::CommandBuffer, +) { + unsafe { + device + .end_command_buffer(command_buffer) + .expect("Failed to record Command Buffer at Ending!"); + } + + let buffers_to_submit = [command_buffer]; + + let sumbit_infos = [vk::SubmitInfo { + s_type: vk::StructureType::SUBMIT_INFO, + p_next: ptr::null(), + wait_semaphore_count: 0, + p_wait_semaphores: ptr::null(), + p_wait_dst_stage_mask: ptr::null(), + command_buffer_count: 1, + p_command_buffers: buffers_to_submit.as_ptr(), + signal_semaphore_count: 0, + p_signal_semaphores: ptr::null(), + }]; + + unsafe { + device + .queue_submit(submit_queue, &sumbit_infos, vk::Fence::null()) + .expect("Failed to Queue Submit!"); + device + .queue_wait_idle(submit_queue) + .expect("Failed to wait Queue idle!"); + device.free_command_buffers(command_pool, &buffers_to_submit); + } +} + +pub fn find_memory_type( + type_filter: u32, + required_properties: vk::MemoryPropertyFlags, + mem_properties: &vk::PhysicalDeviceMemoryProperties, +) -> u32 { + for (i, memory_type) in mem_properties.memory_types.iter().enumerate() { + if (type_filter & (1 << i)) > 0 && memory_type.property_flags.contains(required_properties) + { + return i as u32; + } + } + + panic!("Failed to find suitable memory type!") +} + +pub fn has_stencil_component(format: vk::Format) -> bool { + format == vk::Format::D32_SFLOAT_S8_UINT || format == vk::Format::D24_UNORM_S8_UINT +} + +pub fn copy_buffer_to_image( + device: &ash::Device, + command_pool: vk::CommandPool, + submit_queue: vk::Queue, + buffer: vk::Buffer, + image: vk::Image, + width: u32, + height: u32, +) { + let command_buffer = begin_single_time_command(device, command_pool); + + let buffer_image_regions = [vk::BufferImageCopy { + image_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + image_extent: vk::Extent3D { + width, + height, + depth: 1, + }, + buffer_offset: 0, + buffer_image_height: 0, + buffer_row_length: 0, + image_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, + }]; + + unsafe { + device.cmd_copy_buffer_to_image( + command_buffer, + buffer, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &buffer_image_regions, + ); + } + + end_single_time_command(device, command_pool, submit_queue, command_buffer); +} + +pub fn find_depth_format( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, +) -> vk::Format { + find_supported_format( + instance, + physical_device, + &[ + vk::Format::D32_SFLOAT, + vk::Format::D32_SFLOAT_S8_UINT, + vk::Format::D24_UNORM_S8_UINT, + ], + vk::ImageTiling::OPTIMAL, + vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT, + ) +} + +pub fn find_supported_format( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, + candidate_formats: &[vk::Format], + tiling: vk::ImageTiling, + features: vk::FormatFeatureFlags, +) -> vk::Format { + for &format in candidate_formats.iter() { + let format_properties = + unsafe { instance.get_physical_device_format_properties(physical_device, format) }; + if tiling == vk::ImageTiling::LINEAR + && format_properties.linear_tiling_features.contains(features) + { + return format.clone(); + } else if tiling == vk::ImageTiling::OPTIMAL + && format_properties.optimal_tiling_features.contains(features) + { + return format.clone(); + } + } + + panic!("Failed to find supported format!") +} + +pub fn load_model(model_path: &Path) -> (Vec, Vec) { + let model_obj = tobj::load_obj(model_path).expect("Failed to load model object!"); + + let mut vertices = vec![]; + let mut indices = vec![]; + + let (models, _) = model_obj; + for m in models.iter() { + let mesh = &m.mesh; + + if mesh.texcoords.len() == 0 { + panic!("Missing texture coordinate for the model.") + } + + let total_vertices_count = mesh.positions.len() / 3; + for i in 0..total_vertices_count { + let vertex = VertexV3 { + pos: [ + mesh.positions[i * 3], + mesh.positions[i * 3 + 1], + mesh.positions[i * 3 + 2], + 1.0, + ], + color: [1.0, 1.0, 1.0, 1.0], + tex_coord: [mesh.texcoords[i * 2], mesh.texcoords[i * 2 + 1]], + }; + vertices.push(vertex); + } + + indices = mesh.indices.clone(); + } + + (vertices, indices) +} + +pub fn check_mipmap_support( + instance: &ash::Instance, + physcial_device: vk::PhysicalDevice, + image_format: vk::Format, +) { + let format_properties = + unsafe { instance.get_physical_device_format_properties(physcial_device, image_format) }; + + let is_sample_image_filter_linear_support = format_properties + .optimal_tiling_features + .contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR); + + if is_sample_image_filter_linear_support == false { + panic!("Texture Image format does not support linear blitting!") + } +} diff --git a/src/utility/structures.rs b/src/utility/structures.rs new file mode 100644 index 0000000..8dda352 --- /dev/null +++ b/src/utility/structures.rs @@ -0,0 +1,155 @@ +use ash::vk; +use cgmath::Matrix4; + +use memoffset::offset_of; + +pub struct DeviceExtension { + pub names: [&'static str; 1], + // pub raw_names: [*const i8; 1], +} + +pub struct SurfaceStuff { + pub surface_loader: ash::extensions::khr::Surface, + pub surface: vk::SurfaceKHR, + + pub screen_width: u32, + pub screen_height: u32, +} +pub struct SwapChainStuff { + pub swapchain_loader: ash::extensions::khr::Swapchain, + pub swapchain: vk::SwapchainKHR, + pub swapchain_images: Vec, + pub swapchain_format: vk::Format, + pub swapchain_extent: vk::Extent2D, +} + +pub struct SwapChainSupportDetail { + pub capabilities: vk::SurfaceCapabilitiesKHR, + pub formats: Vec, + pub present_modes: Vec, +} + +pub struct QueueFamilyIndices { + pub graphics_family: Option, + pub present_family: Option, +} + +impl QueueFamilyIndices { + pub fn new() -> QueueFamilyIndices { + QueueFamilyIndices { + graphics_family: None, + present_family: None, + } + } + + pub fn is_complete(&self) -> bool { + self.graphics_family.is_some() && self.present_family.is_some() + } +} + +pub struct SyncObjects { + pub image_available_semaphores: Vec, + pub render_finished_semaphores: Vec, + pub inflight_fences: Vec, +} + +#[repr(C)] +#[derive(Clone, Debug, Copy)] +pub struct UniformBufferObject { + pub model: Matrix4, + pub view: Matrix4, + pub proj: Matrix4, +} + +#[repr(C)] +#[derive(Clone, Debug, Copy)] +pub struct VertexV1 { + pub pos: [f32; 2], + pub color: [f32; 3], +} +impl VertexV1 { + pub fn get_binding_description() -> [vk::VertexInputBindingDescription; 1] { + [vk::VertexInputBindingDescription { + binding: 0, + stride: ::std::mem::size_of::() as u32, + input_rate: vk::VertexInputRate::VERTEX, + }] + } + + pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { + [ + vk::VertexInputAttributeDescription { + binding: 0, + location: 0, + format: vk::Format::R32G32_SFLOAT, + offset: offset_of!(VertexV1, pos) as u32, + }, + vk::VertexInputAttributeDescription { + binding: 0, + location: 1, + format: vk::Format::R32G32B32_SFLOAT, + offset: offset_of!(VertexV1, color) as u32, + }, + ] + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct VertexV3 { + pub pos: [f32; 4], + pub color: [f32; 4], + pub tex_coord: [f32; 2], +} +impl VertexV3 { + pub fn get_binding_descriptions() -> [vk::VertexInputBindingDescription; 1] { + [vk::VertexInputBindingDescription { + binding: 0, + stride: ::std::mem::size_of::() as u32, + input_rate: vk::VertexInputRate::VERTEX, + }] + } + + pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 3] { + [ + vk::VertexInputAttributeDescription { + binding: 0, + location: 0, + format: vk::Format::R32G32B32A32_SFLOAT, + offset: offset_of!(Self, pos) as u32, + }, + vk::VertexInputAttributeDescription { + binding: 0, + location: 1, + format: vk::Format::R32G32B32A32_SFLOAT, + offset: offset_of!(Self, color) as u32, + }, + vk::VertexInputAttributeDescription { + binding: 0, + location: 2, + format: vk::Format::R32G32_SFLOAT, + offset: offset_of!(Self, tex_coord) as u32, + }, + ] + } +} + +pub const RECT_VERTICES_DATA: [VertexV1; 4] = [ + VertexV1 { + pos: [-0.5, -0.5], + color: [1.0, 0.0, 0.0], + }, + VertexV1 { + pos: [0.5, -0.5], + color: [0.0, 1.0, 0.0], + }, + VertexV1 { + pos: [0.5, 0.5], + color: [0.0, 0.0, 1.0], + }, + VertexV1 { + pos: [-0.5, 0.5], + color: [1.0, 1.0, 1.0], + }, +]; +pub const RECT_INDICES_DATA: [u32; 6] = [0, 1, 2, 2, 3, 0]; diff --git a/src/utility/tools.rs b/src/utility/tools.rs new file mode 100644 index 0000000..8bcc372 --- /dev/null +++ b/src/utility/tools.rs @@ -0,0 +1,46 @@ +use std::ffi::CStr; +use std::os::raw::c_char; +use std::path::Path; + +/// Helper function to convert [c_char; SIZE] to string +pub fn vk_to_string(raw_string_array: &[c_char]) -> String { + // Implementation 1 + // let end = '\0' as u8; + // + // let mut content: Vec = vec![]; + // + // for ch in raw_string_array.iter() { + // let ch = (*ch) as u8; + // + // if ch != end { + // content.push(ch); + // } else { + // break + // } + // } + // + // String::from_utf8(content) + // .expect("Failed to convert vulkan raw string") + + // Implementation 2 + let raw_string = unsafe { + let pointer = raw_string_array.as_ptr(); + CStr::from_ptr(pointer) + }; + + raw_string + .to_str() + .expect("Failed to convert vulkan raw string.") + .to_owned() +} + +pub fn read_shader_code(shader_path: &Path) -> Vec { + use std::fs::File; + use std::io::Read; + + let spv_file = + File::open(shader_path).expect(&format!("Failed to find spv file at {:?}", shader_path)); + let bytes_code: Vec = spv_file.bytes().filter_map(|byte| byte.ok()).collect(); + + bytes_code +} diff --git a/src/utility/window.rs b/src/utility/window.rs new file mode 100644 index 0000000..dc901ae --- /dev/null +++ b/src/utility/window.rs @@ -0,0 +1,97 @@ +use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; + + +const IS_PAINT_FPS_COUNTER: bool = true; + +pub fn init_window( + event_loop: &EventLoop<()>, + title: &str, + width: u32, + height: u32, +) -> winit::window::Window { + winit::window::WindowBuilder::new() + .with_title(title) + .with_inner_size(winit::dpi::LogicalSize::new(width, height)) + .build(event_loop) + .expect("Failed to create window.") +} + +pub trait VulkanApp { + fn draw_frame(&mut self, delta_time: f32); + fn recreate_swapchain(&mut self); + fn cleanup_swapchain(&self); + fn wait_device_idle(&self); + fn resize_framebuffer(&mut self); + fn window_ref(&self) -> &winit::window::Window; +} + +pub struct ProgramProc { + pub event_loop: EventLoop<()>, +} + +impl ProgramProc { + + pub fn new() -> ProgramProc { + // init window stuff + let event_loop = EventLoop::new(); + + ProgramProc { event_loop } + } + + pub fn main_loop(self, mut vulkan_app: A) { + + let mut tick_counter = super::fps_limiter::FPSLimiter::new(); + + self.event_loop.run(move |event, _, control_flow| { + + match event { + | Event::WindowEvent { event, .. } => { + match event { + | WindowEvent::CloseRequested => { + vulkan_app.wait_device_idle(); + *control_flow = ControlFlow::Exit + }, + | WindowEvent::KeyboardInput { input, .. } => { + match input { + | KeyboardInput { virtual_keycode, state, .. } => { + match (virtual_keycode, state) { + | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { + vulkan_app.wait_device_idle(); + *control_flow = ControlFlow::Exit + }, + | _ => {}, + } + }, + } + }, + | WindowEvent::Resized(_new_size) => { + vulkan_app.wait_device_idle(); + vulkan_app.resize_framebuffer(); + }, + | _ => {}, + } + }, + | Event::MainEventsCleared => { + vulkan_app.window_ref().request_redraw(); + }, + | Event::RedrawRequested(_window_id) => { + let delta_time = tick_counter.delta_time(); + vulkan_app.draw_frame(delta_time); + + if IS_PAINT_FPS_COUNTER { + print!("FPS: {}\r", tick_counter.fps()); + } + + tick_counter.tick_frame(); + }, + | Event::LoopDestroyed => { + vulkan_app.wait_device_idle(); + }, + _ => (), + } + + }) + } + +}