#![allow(unused)] use winit::{ application::ApplicationHandler, dpi::{LogicalSize, PhysicalSize}, error as werror, event::{DeviceEvent, DeviceId, Event, WindowEvent}, event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, raw_window_handle::HasDisplayHandle, raw_window_handle::HasWindowHandle, window::{Window, WindowId}, }; use vulkanalia::{ bytecode::{Bytecode, BytecodeError}, loader::{LibloadingLoader, LoaderError, LIBRARY}, prelude::v1_0::*, vk::{ExtDebugUtilsExtension, KhrSurfaceExtension, KhrSwapchainExtension}, window as vw, Version, }; use std::{collections::HashSet, ffi::CStr, os::raw::c_void}; use super::{RendererBackend, TextSpan}; use super::error::{MainError, SuitabilityError}; const PORTABILITY_MACOS_VERSION: Version = Version::new(1, 3, 216); const VALIDATION_ENABLED: bool = cfg!(debug_assertions); const VALIDATION_LAYER: vk::ExtensionName = vk::ExtensionName::from_bytes("VK_LAYER_KHRONOS_validation".as_bytes()); const DEVICE_EXTENSIONS: &[vk::ExtensionName] = &[vk::KHR_SWAPCHAIN_EXTENSION.name]; const MAX_FRAMES_IN_FLIGHT: usize = 2; #[derive(Debug)] pub struct VulkanRenderer { window: Option, entry: Option, instance: Option, data: AppData, device: Option, frame: usize, resized: bool, resumed: bool, pending_recreation: bool, minimized: bool, toupdate: bool, window_size_at_recreate_request: PhysicalSize, } impl VulkanRenderer { pub fn new() -> Self { let window = None; let entry = None; let data = Default::default(); let device = None; let instance = None; let frame = 0usize; let resized = false; let resumed = false; let pending_recreation = false; let minimized = false; let toupdate = true; let window_size_at_recreate_request = PhysicalSize::::new(0, 0); Self { window, entry, instance, data, device, frame, resized, resumed, pending_recreation, minimized, toupdate, window_size_at_recreate_request, } } unsafe fn recreate_swapchain(&mut self) -> Result<(), MainError> { dbg!("start recreating"); unsafe { if let Some(device) = &self.device { dbg!(device); device.device_wait_idle()?; } dbg!("before destroy swapchain"); self.destroy_swapchain(); dbg!("after destroy swapchain"); // // --- Destroy and recreate the Vulkan surface --- // if let (Some(instance), Some(window)) = (&self.instance, &self.window) { // dbg!("Creating new surface"); // let window_handle = window.window_handle().unwrap(); // let display_handle = window.display_handle().unwrap(); // dbg!(&display_handle); // dbg!(&window_handle); // // Use vulkanalia::window::create_surface with the current window handles // self.data.surface = vw::create_surface(instance, &display_handle, &window_handle)?; // dbg!("New surface created."); // } else { // // This case should ideally not be reached if the application is properly initialized. // panic!("Instance or Window is None during surface recreation"); // } // // -- END -- if let (Some(device), Some(instance), Some(window)) = (&self.device, &self.instance, &self.window) { dbg!("before create_swapchain"); create_swapchain(window, instance, device, &mut self.data)?; dbg!("after create_swapchain"); dbg!("before create_swapchain_image_views"); create_swapchain_image_views(device, &mut self.data)?; dbg!("after create_swapchain_image_views"); dbg!("before create_render_pass"); create_render_pass(instance, device, &mut self.data)?; dbg!("after create_render_pass"); dbg!("before create_pipeline"); create_pipeline(device, &mut self.data)?; dbg!("after create_pipeline"); dbg!("before create_framebuffers"); create_framebuffers(device, &mut self.data)?; dbg!("after create_framebuffers"); dbg!("before create_command_buffers"); create_command_buffers(device, &mut self.data)?; dbg!("after create_command_buffers"); } dbg!("after create groups"); self.data .images_in_flight .resize(self.data.swapchain_images.len(), vk::Fence::null()); dbg!("end recreating"); Ok(()) } } unsafe fn destroy_swapchain(&mut self) { unsafe { if let Some(device) = &self.device { for buffer in self.data.framebuffers.iter().copied() { device.destroy_framebuffer(buffer, None); } self.data.framebuffers.clear(); device.free_command_buffers(self.data.command_pool, &self.data.command_buffers); device.destroy_pipeline(self.data.pipeline, None); device.destroy_pipeline_layout(self.data.pipeline_layout, None); device.destroy_render_pass(self.data.render_pass, None); for view in self.data.swapchain_image_views.iter() { device.destroy_image_view(*view, None); } device.destroy_swapchain_khr(self.data.swapchain, None); } } } unsafe fn render(&mut self) -> Result<(), MainError> { unsafe { if let (Some(device), Some(window)) = (&self.device, &self.window) { device.wait_for_fences( &[self.data.in_flight_fences[self.frame]], true, u64::MAX, )?; let acquire_image_result = device.acquire_next_image_khr( self.data.swapchain, u64::MAX, self.data.image_available_sp[self.frame], vk::Fence::null(), ); // dbg!("Acquired next image"); let image_index = match acquire_image_result { Ok((image_index, _)) => image_index, Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return self.recreate_swapchain(), Err(code) => return Err(code.into()), }; // dbg!("Image index obtained", image_index); if !self.data.images_in_flight[image_index as usize].is_null() { // dbg!("Image in flight is not null"); device.wait_for_fences( &[self.data.images_in_flight[image_index as usize]], true, u64::MAX, )?; // dbg!("Waited for image in flight fence"); } self.data.images_in_flight[image_index as usize] = self.data.in_flight_fences[self.frame]; // dbg!("Updated images_in_flight"); let waiting_sps = [self.data.image_available_sp[self.frame]]; let waiting_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; let command_buffer = [self.data.command_buffers[image_index as usize]]; let render_sps = [self.data.render_finished_sp[self.frame]]; let submit_info = vk::SubmitInfo::builder() .wait_semaphores(&waiting_sps) .wait_dst_stage_mask(&waiting_stages) .command_buffers(&command_buffer) .signal_semaphores(&render_sps); // dbg!("Submitting queue"); device.reset_fences(&[self.data.in_flight_fences[self.frame]])?; device.queue_submit( self.data.gqueue, &[submit_info], self.data.in_flight_fences[self.frame], )?; // dbg!("Queue submitted"); let swapchains = [self.data.swapchain]; let image_indices = [image_index]; let presentation_info = vk::PresentInfoKHR::builder() .wait_semaphores(&render_sps) .swapchains(&swapchains) .image_indices(&image_indices); // dbg!("Presenting queue"); let present_result = device.queue_present_khr(self.data.pqueue, &presentation_info); // dbg!("Queue presented"); if self.resized || matches!( present_result, Ok(vk::SuccessCode::SUBOPTIMAL_KHR) | Err(vk::ErrorCode::OUT_OF_DATE_KHR) ) { dbg!("Recreating swapchain due to resize/suboptimal"); self.resized = false; self.recreate_swapchain()?; } else if let Err(e) = present_result { dbg!("Present result error", &e); return Err(e.into()); } self.frame = (self.frame + 1).rem_euclid(MAX_FRAMES_IN_FLIGHT); dbg!("Render function finished successfully"); } Ok(()) } } unsafe fn destroy(&mut self) { unsafe { self.destroy_swapchain(); if let (Some(device), Some(instance)) = (&self.device, &self.instance) { for f in self.data.in_flight_fences.iter() { device.destroy_fence(*f, None); } for s in self.data.image_available_sp.iter() { device.destroy_semaphore(*s, None); } for s in self.data.render_finished_sp.iter() { device.destroy_semaphore(*s, None); } device.destroy_command_pool(self.data.command_pool, None); device.destroy_device(None); instance.destroy_surface_khr(self.data.surface, None); if VALIDATION_ENABLED { instance.destroy_debug_utils_messenger_ext(self.data.messenger, None); } instance.destroy_instance(None); } } } fn handle_resize(&mut self, new_size: PhysicalSize) { if new_size.width == 0 || new_size.height == 0 { self.minimized = true; } else { self.minimized = false; self.pending_recreation = true; self.window_size_at_recreate_request = new_size; } } } impl ApplicationHandler for VulkanRenderer { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.resumed { // redundant signal, just ignore this return; } self.resumed = true; // create the window here let attrs = Window::default_attributes() .with_title("vulkan") .with_decorations(true) .with_resizable(true) .with_inner_size(LogicalSize::new(1024, 720)); let window = event_loop.create_window(attrs).unwrap(); let mut data = AppData::default(); unsafe { let loader = LibloadingLoader::new(LIBRARY) .map_err(|e| MainError::LibLoadError(e.to_string())) .unwrap(); let entry = Entry::new(loader).unwrap(); let instance = create_instance(&window, &entry, &mut data).unwrap(); data.surface = vw::create_surface(&instance, &window, &window).unwrap(); pick_physical_device(&instance, &mut data).unwrap(); let device = create_logical_device(&entry, &instance, &mut data).unwrap(); create_swapchain(&window, &instance, &device, &mut data).unwrap(); create_swapchain_image_views(&device, &mut data).unwrap(); create_render_pass(&instance, &device, &mut data).unwrap(); create_pipeline(&device, &mut data).unwrap(); create_framebuffers(&device, &mut data).unwrap(); create_command_pool(&instance, &device, &mut data).unwrap(); create_command_buffers(&device, &mut data).unwrap(); create_synchronized_objects(&device, &mut data).unwrap(); let frame = 0usize; let resized = false; self.window = Some(window); self.device = Some(device); self.instance = Some(instance); self.data = data; } dbg!("end of resumed"); } fn window_event( &mut self, event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent, ) { match event { WindowEvent::CloseRequested => { event_loop.exit(); // unsafe { // if let Some(device) = &self.device { // device.device_wait_idle().unwrap(); // self.destroy(); // } // } } WindowEvent::RedrawRequested if self.toupdate && !self.minimized && !event_loop.exiting() => { dbg!("redraw starts"); self.toupdate = false; unsafe { self.render() }.unwrap(); } WindowEvent::Resized(new_size) => { dbg!("before handle resize"); self.handle_resize(new_size); dbg!("after handle resize"); } _ => (), } } fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { if self.toupdate { self.window.as_ref().unwrap().request_redraw(); } // if self.window.is_some() { // let current_size = self.window.as_ref().unwrap().inner_size(); // // Check if the window size has stabilized AND is not zero-sized // if self.window_size_at_recreate_request.width == current_size.width // && self.window_size_at_recreate_request.height == current_size.height // && current_size.width > 0 // && current_size.height > 0 // { // dbg!("Attempting deferred swapchain recreation..."); // dbg!(current_size, self.window_size_at_recreate_request); // unsafe { // if let Err(e) = self.recreate_swapchain() { // panic!("Error during deferred swapchain recreation: {:?}", e); // } else { // #[cfg(debug_assertions)] // dbg!("Deferred swapchain recreation successful."); // self.pending_recreation = false; // Reset the flag after successful recreation // self.window.as_ref().unwrap().request_redraw(); // Request a redraw to render with the new swapchain // } // } // } else { // #[cfg(debug_assertions)] // dbg!("Still waiting for a stable, non-zero window size for recreation. Current: {:?}, Requested: {:?}", current_size, self.window_size_at_recreate_request); // } // } else { // // Handle case where window might be closed before recreation // #[cfg(debug_assertions)] // dbg!("Window is None, cannot perform deferred recreation. Resetting recreate_pending flag."); // self.pending_recreation = false; // } } // Always request redraw if you want continuous rendering, or only // when needed. If you only redraw when changes occur, // `request_redraw()` after successful recreation is key. // Uncomment if you want continuous redraws even when idle // event_loop.request_redraw(); } impl RendererBackend for VulkanRenderer { fn name(&self) -> &str { "vulkan" } fn init(&mut self, width: i32, height: i32) -> bool { // Initialize Vulkan instance, device, swapchain, etc. println!("Initializing Vulkan backend..."); true } fn clear(&mut self) { // Clear frame } fn draw_text(&mut self, _span: &TextSpan) { // Placeholder: draw text with Vulkan } fn present(&mut self) { // Present frame } fn shutdown(&mut self) { println!("Shutting down Vulkan backend..."); } } #[derive(Clone, Debug, Default)] struct AppData { surface: vk::SurfaceKHR, messenger: vk::DebugUtilsMessengerEXT, physical_device: vk::PhysicalDevice, gqueue: vk::Queue, pqueue: vk::Queue, swapchain: vk::SwapchainKHR, swapchain_images: Vec, swapchain_format: vk::Format, swapchain_extent: vk::Extent2D, swapchain_image_views: Vec, render_pass: vk::RenderPass, pipeline_layout: vk::PipelineLayout, pipeline: vk::Pipeline, framebuffers: Vec, command_pool: vk::CommandPool, command_buffers: Vec, image_available_sp: Vec, render_finished_sp: Vec, in_flight_fences: Vec, images_in_flight: Vec, } unsafe fn create_instance( window: &Window, entry: &Entry, data: &mut AppData, ) -> Result { unsafe { let application_info = vk::ApplicationInfo::builder() .application_name("vulkan-tuto\0".as_bytes()) .application_version(vk::make_version(1, 0, 0)) .engine_name("No engine\0".as_bytes()) .engine_version(vk::make_version(1, 0, 0)) .api_version(vk::make_version(1, 0, 0)); // for s in vw::get_required_instance_extensions(window).iter() { // println!("required instance extension: {s:?}"); // } let available_layers = entry .enumerate_instance_layer_properties()? .iter() .map(|l| l.layer_name) .collect::>(); // println!("layers count: {}", available_layers.len()); // for layer in available_layers.iter() { // println!("layer: {layer:?}"); // } if VALIDATION_ENABLED && !available_layers.contains(&VALIDATION_LAYER) { dbg!(); return Err(MainError::ValidationNotSupported); } let mut required_extensions = vw::get_required_instance_extensions(window) .iter() .map(|s| s.as_ptr()) .collect::>(); if VALIDATION_ENABLED { required_extensions.push(vk::EXT_DEBUG_UTILS_EXTENSION.name.as_ptr()); } if cfg!(target_os = "macos") && entry.version()? >= PORTABILITY_MACOS_VERSION { println!("Enabling extensions for Mac OS portability..."); required_extensions.extend([ vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION .name .as_ptr(), vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name.as_ptr(), ]); } let mut info = vk::InstanceCreateInfo::builder() .application_info(&application_info) .enabled_extension_names(&required_extensions); let mut debug_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() .message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all()) .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) .user_callback(Some(debug_callback)); if cfg!(target_os = "macos") && entry.version()? >= PORTABILITY_MACOS_VERSION { info = info.flags(vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR); } let layers = vec![VALIDATION_LAYER.as_ptr()]; if VALIDATION_ENABLED { info = info.enabled_layer_names(&layers); info = info.push_next(&mut debug_info); } let instance = entry.create_instance(&info, None)?; if VALIDATION_ENABLED { data.messenger = instance.create_debug_utils_messenger_ext(&debug_info, None)?; } Ok(instance) } } #[derive(Debug, Clone, Copy)] struct QueueFamilyIndices { graphics: u32, presentation: u32, } impl QueueFamilyIndices { unsafe fn get( instance: &Instance, data: &AppData, physical_device: vk::PhysicalDevice, ) -> Result { unsafe { dbg!("start getting", &physical_device); let properties = instance.get_physical_device_queue_family_properties(physical_device); // for property in properties.iter() { // println!("property: {property:?}"); // } dbg!("before graphics"); let graphics = properties .iter() .position(|p| p.queue_flags.contains(vk::QueueFlags::GRAPHICS)) .map(|i| i as u32); dbg!("after graphics"); let mut presentation = None; for (index, _property) in properties.iter().enumerate() { let index = index as u32; if instance.get_physical_device_surface_support_khr( physical_device, index, data.surface, )? { presentation = Some(index); break; } } if let (Some(graphics), Some(presentation)) = (graphics, presentation) { Ok(Self { graphics, presentation, }) } else { Err(MainError::Suitability(SuitabilityError::NoQueuefamily)) } } } } #[derive(Debug, Clone)] struct SwapchainSupport { capabilities: vk::SurfaceCapabilitiesKHR, formats: Vec, pmodes: Vec, } impl SwapchainSupport { unsafe fn get( instance: &Instance, data: &AppData, physical_device: vk::PhysicalDevice, ) -> Result { unsafe { let capabilities = instance .get_physical_device_surface_capabilities_khr(physical_device, data.surface)?; let formats = instance.get_physical_device_surface_formats_khr(physical_device, data.surface)?; let pmodes = instance .get_physical_device_surface_present_modes_khr(physical_device, data.surface)?; Ok(Self { capabilities, formats, pmodes, }) } } } unsafe fn check_physical_device_extensions( instance: &Instance, physical_device: vk::PhysicalDevice, ) -> Result<(), MainError> { unsafe { let extensions: HashSet<_> = instance .enumerate_device_extension_properties(physical_device, None)? .iter() .map(|e| e.extension_name) .collect(); // for ext in extensions.iter() { // println!("support extension {ext}"); // } if !DEVICE_EXTENSIONS.iter().all(|e| extensions.contains(e)) { Err(MainError::NoExtension) } else { Ok(()) } } } fn get_swapchain_surace_format(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR { if let Some(format) = formats.iter().find(|format| { format.format == vk::Format::B8G8R8A8_SRGB && format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR }) { *format } else { // panic is desired here *formats.first().unwrap() } } fn get_swapchain_pmode(pmodes: &[vk::PresentModeKHR]) -> vk::PresentModeKHR { pmodes .iter() .find(|pmode| **pmode == vk::PresentModeKHR::MAILBOX) .copied() .unwrap_or(vk::PresentModeKHR::FIFO) } fn get_swapchain_extent(window: &Window, capabilities: vk::SurfaceCapabilitiesKHR) -> vk::Extent2D { if capabilities.current_extent.width != u32::MAX { capabilities.current_extent } else { let min = capabilities.min_image_extent; let max = capabilities.max_image_extent; vk::Extent2D::builder() .width(window.inner_size().width.clamp(min.width, max.width)) .height(window.inner_size().height.clamp(min.height, max.height)) .build() } } unsafe fn create_swapchain( window: &Window, instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<(), MainError> { dbg!("start creating swapchain"); unsafe { let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?; dbg!("after getting indices"); let support = SwapchainSupport::get(instance, data, data.physical_device)?; dbg!("after getting support"); let format = get_swapchain_surace_format(&support.formats); let pmode = get_swapchain_pmode(&support.pmodes); let extent = get_swapchain_extent(window, support.capabilities); dbg!("after getting suface format, pmode, and extent"); let min_image_count = support.capabilities.min_image_count; let mut max_image_count = support.capabilities.max_image_count; if max_image_count == 0 { max_image_count = u32::MAX; } let image_count = (min_image_count + 1).clamp(min_image_count, max_image_count); let (image_sharing_mode, queue_family_indices) = if indices.graphics != indices.presentation { ( vk::SharingMode::CONCURRENT, vec![indices.graphics, indices.presentation], ) } else { (vk::SharingMode::EXCLUSIVE, vec![]) }; dbg!("before info"); let info = vk::SwapchainCreateInfoKHR::builder() .surface(data.surface) .min_image_count(image_count) .image_format(format.format) .image_color_space(format.color_space) .image_extent(extent) .image_array_layers(1) .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) .image_sharing_mode(image_sharing_mode) .queue_family_indices(&queue_family_indices) .pre_transform(support.capabilities.current_transform) .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) .present_mode(pmode) .clipped(true) .old_swapchain(vk::SwapchainKHR::null()); dbg!("before create khr"); data.swapchain = device.create_swapchain_khr(&info, None)?; dbg!("before get image khr"); data.swapchain_images = device.get_swapchain_images_khr(data.swapchain)?; data.swapchain_format = format.format; data.swapchain_extent = extent; dbg!("end of create_swapchain"); Ok(()) } } unsafe fn create_swapchain_image_views( device: &Device, data: &mut AppData, ) -> Result<(), MainError> { unsafe { data.swapchain_image_views = data .swapchain_images .iter() .map(|image| unsafe { let components = vk::ComponentMapping::builder() .r(vk::ComponentSwizzle::IDENTITY) .g(vk::ComponentSwizzle::IDENTITY) .b(vk::ComponentSwizzle::IDENTITY) .a(vk::ComponentSwizzle::IDENTITY); let subresource_range = vk::ImageSubresourceRange::builder() .aspect_mask(vk::ImageAspectFlags::COLOR) .base_mip_level(0) .level_count(1) .base_array_layer(0) .layer_count(1); let info = vk::ImageViewCreateInfo::builder() .image(*image) .view_type(vk::ImageViewType::_2D) .format(data.swapchain_format) .components(components) .subresource_range(subresource_range); device.create_image_view(&info, None) }) .collect::, vk::ErrorCode>>()?; Ok(()) } } unsafe fn create_shader_module( device: &Device, bytecode: &[u8], ) -> Result { unsafe { let bytecode = Bytecode::new(bytecode)?; let info = vk::ShaderModuleCreateInfo::builder() .code_size(bytecode.code_size()) .code(bytecode.code()); device.create_shader_module(&info, None).map_err(Into::into) } } unsafe fn create_render_pass( _instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<(), MainError> { unsafe { let color_attachment = vk::AttachmentDescription::builder() .format(data.swapchain_format) .samples(vk::SampleCountFlags::_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_reference = vk::AttachmentReference::builder() .attachment(0) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let color_attachment_references = [color_attachment_reference]; let sub_dependency = vk::SubpassDependency::builder() .src_subpass(vk::SUBPASS_EXTERNAL) .dst_subpass(0u32) .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .src_access_mask(vk::AccessFlags::empty()) .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE); let subpass = vk::SubpassDescription::builder() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .color_attachments(&color_attachment_references); let attachments = [color_attachment]; let subpasses = [subpass]; let dependencies = [sub_dependency]; let info = vk::RenderPassCreateInfo::builder() .attachments(&attachments) .subpasses(&subpasses) .dependencies(&dependencies); data.render_pass = device.create_render_pass(&info, None)?; Ok(()) } } unsafe fn create_pipeline(device: &Device, data: &mut AppData) -> Result<(), MainError> { unsafe { let vertex_shader = include_bytes!("../../shaders/shader.vert.spv"); let fragment_shader = include_bytes!("../../shaders/shader.frag.spv"); let vertex_shader_module = create_shader_module(device, vertex_shader.as_slice())?; let fragment_shader_module = create_shader_module(device, fragment_shader.as_slice())?; let vertex_stage = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::VERTEX) .module(vertex_shader_module) .name("main\0".as_bytes()); let fragment_stage = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::FRAGMENT) .module(fragment_shader_module) .name("main\0".as_bytes()); let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::builder(); let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo::builder() .topology(vk::PrimitiveTopology::TRIANGLE_LIST) .primitive_restart_enable(false); let viewport = vk::Viewport::builder() .x(0f32) .y(0f32) .width(data.swapchain_extent.width as f32) .height(data.swapchain_extent.height as f32) .min_depth(0f32) .max_depth(1f32); let scissor = vk::Rect2D::builder() .offset(vk::Offset2D::builder().x(0).y(0).build()) .extent(data.swapchain_extent); let viewports = &[viewport]; let scissors = &[scissor]; let viewport_state = vk::PipelineViewportStateCreateInfo::builder() .viewports(viewports) .scissors(scissors); let rasterization_state = vk::PipelineRasterizationStateCreateInfo::builder() .depth_clamp_enable(false) .rasterizer_discard_enable(false) .polygon_mode(vk::PolygonMode::FILL) .line_width(1f32) .cull_mode(vk::CullModeFlags::BACK) .front_face(vk::FrontFace::CLOCKWISE) .depth_bias_enable(false); let multisample_state = vk::PipelineMultisampleStateCreateInfo::builder() .sample_shading_enable(false) .rasterization_samples(vk::SampleCountFlags::_1); let attachment_state = vk::PipelineColorBlendAttachmentState::builder() .color_write_mask(vk::ColorComponentFlags::all()) .blend_enable(false) .src_color_blend_factor(vk::BlendFactor::SRC_ALPHA) .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) .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 attachment_states = [attachment_state]; let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder() .logic_op_enable(false) .logic_op(vk::LogicOp::COPY) .attachments(&attachment_states) .blend_constants([0f32, 0f32, 0f32, 0f32]); let layout_info = vk::PipelineLayoutCreateInfo::builder(); data.pipeline_layout = device.create_pipeline_layout(&layout_info, None)?; let stages = [vertex_stage, fragment_stage]; let info = vk::GraphicsPipelineCreateInfo::builder() .stages(&stages) .vertex_input_state(&vertex_input_state) .input_assembly_state(&input_assembly_state) .viewport_state(&viewport_state) .rasterization_state(&rasterization_state) .multisample_state(&multisample_state) .color_blend_state(&color_blend_state) .layout(data.pipeline_layout) .render_pass(data.render_pass) .subpass(0); data.pipeline = device .create_graphics_pipelines(vk::PipelineCache::null(), &[info], None)? .0[0]; device.destroy_shader_module(vertex_shader_module, None); device.destroy_shader_module(fragment_shader_module, None); Ok(()) } } unsafe fn create_framebuffers(device: &Device, data: &mut AppData) -> Result<(), MainError> { unsafe { data.framebuffers = data .swapchain_image_views .iter() .map(|view| { let attachments = [*view]; let info = vk::FramebufferCreateInfo::builder() .render_pass(data.render_pass) .attachments(&attachments) .width(data.swapchain_extent.width) .height(data.swapchain_extent.height) .layers(1); device.create_framebuffer(&info, None) }) .collect::, _>>()?; Ok(()) } } unsafe fn create_command_pool( instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<(), MainError> { unsafe { let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?; let info = vk::CommandPoolCreateInfo::builder() .flags(vk::CommandPoolCreateFlags::empty()) .queue_family_index(indices.graphics); data.command_pool = device.create_command_pool(&info, None)?; #[cfg(debug_assertions)] assert_ne!( data.command_pool, vk::CommandPool::null(), "null command pool" ); Ok(()) } } unsafe fn create_command_buffers(device: &Device, data: &mut AppData) -> Result<(), MainError> { unsafe { let allocate_info = vk::CommandBufferAllocateInfo::builder() .command_pool(data.command_pool) .level(vk::CommandBufferLevel::PRIMARY) .command_buffer_count(data.framebuffers.len() as u32); data.command_buffers = device.allocate_command_buffers(&allocate_info)?; for (i, buffer) in data.command_buffers.iter().enumerate() { let inheritance = vk::CommandBufferInheritanceInfo::builder(); let info = vk::CommandBufferBeginInfo::builder() .flags(vk::CommandBufferUsageFlags::empty()) .inheritance_info(&inheritance); device.begin_command_buffer(*buffer, &info)?; let render_area = vk::Rect2D::builder() .offset(vk::Offset2D::default()) .extent(data.swapchain_extent); let color_clear_value = vk::ClearValue { color: vk::ClearColorValue { float32: [0.3f32, 1f32, 1f32, 0.35f32], }, }; let clear_values = [color_clear_value]; let info = vk::RenderPassBeginInfo::builder() .render_pass(data.render_pass) .framebuffer(data.framebuffers[i]) .render_area(render_area) .clear_values(&clear_values); device.cmd_begin_render_pass(*buffer, &info, vk::SubpassContents::INLINE); // device.cmd_bind_pipeline(*buffer, vk::PipelineBindPoint::GRAPHICS, data.pipeline); // device.cmd_draw(*buffer, 3, 1, 0, 0); device.cmd_end_render_pass(*buffer); device.end_command_buffer(*buffer)?; } Ok(()) } } unsafe fn create_synchronized_objects( device: &Device, data: &mut AppData, ) -> Result<(), MainError> { unsafe { let info = vk::SemaphoreCreateInfo::builder(); let fence_info = vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED); data.image_available_sp = std::iter::repeat_with(|| device.create_semaphore(&info, None)) .take(MAX_FRAMES_IN_FLIGHT) .collect::, _>>()?; data.render_finished_sp = std::iter::repeat_with(|| device.create_semaphore(&info, None)) .take(MAX_FRAMES_IN_FLIGHT) .collect::, _>>()?; data.in_flight_fences = std::iter::repeat_with(|| device.create_fence(&fence_info, None)) .take(MAX_FRAMES_IN_FLIGHT) .collect::, _>>()?; data.images_in_flight = data .swapchain_images .iter() .map(|_| vk::Fence::null()) .collect(); Ok(()) } } unsafe fn check_physical_device( instance: &Instance, data: &mut AppData, physical_device: vk::PhysicalDevice, ) -> Result<(), MainError> { unsafe { let properties = instance.get_physical_device_properties(physical_device); let _features = instance.get_physical_device_features(physical_device); println!("device type = {:?}", properties.device_type); QueueFamilyIndices::get(instance, data, physical_device)?; check_physical_device_extensions(instance, physical_device)?; let support = SwapchainSupport::get(instance, data, physical_device)?; if support.formats.is_empty() || support.pmodes.is_empty() { return Err(MainError::InsufficientSwapchainSupport); } // println!("Swapchain support: {support:?}"); Ok(()) } } unsafe fn pick_physical_device(instance: &Instance, data: &mut AppData) -> Result<(), MainError> { unsafe { for device in instance.enumerate_physical_devices()? { #[cfg(debug_assertions)] let properties = instance.get_physical_device_properties(device); if let Err(e) = check_physical_device(instance, data, device) { #[cfg(debug_assertions)] println!( "Skipping device {} due to error {e}", properties.device_name ); } else { #[cfg(debug_assertions)] println!("Selecting device {}", properties.device_name); data.physical_device = device; return Ok(()); } } Err(MainError::NoDevice) } } unsafe fn create_logical_device( entry: &Entry, instance: &Instance, data: &mut AppData, ) -> Result { unsafe { let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?; let mut unique_indices = HashSet::new(); unique_indices.insert(indices.graphics); unique_indices.insert(indices.presentation); let queue_priorities = [1.0f32]; let layers = if VALIDATION_ENABLED { vec![VALIDATION_LAYER.as_ptr()] } else { Vec::new() }; let mut extensions: Vec<_> = DEVICE_EXTENSIONS.iter().map(|ext| ext.as_ptr()).collect(); if cfg!(target_os = "macos") && entry.version()? >= PORTABILITY_MACOS_VERSION { extensions.push(vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr()); } let features = vk::PhysicalDeviceFeatures::builder(); let queue_infos: Vec<_> = unique_indices .iter() .map(|index| { vk::DeviceQueueCreateInfo::builder() .queue_family_index(*index) .queue_priorities(&queue_priorities) }) .collect(); let info = vk::DeviceCreateInfo::builder() .queue_create_infos(&queue_infos) .enabled_layer_names(&layers) .enabled_extension_names(&extensions) .enabled_features(&features); let device = instance.create_device(data.physical_device, &info, None)?; data.gqueue = device.get_device_queue(indices.graphics, 0); data.pqueue = device.get_device_queue(indices.presentation, 0); Ok(device) } } extern "system" fn debug_callback( severity: vk::DebugUtilsMessageSeverityFlagsEXT, type_: vk::DebugUtilsMessageTypeFlagsEXT, data: *const vk::DebugUtilsMessengerCallbackDataEXT, _: *mut c_void, ) -> vk::Bool32 { let data = unsafe { *data }; let message = unsafe { CStr::from_ptr(data.message) }.to_string_lossy(); if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::ERROR { println!("error: ({type_:?}), {message}"); } else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING { println!("warning: ({type_:?}), {message}"); } else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::INFO { // #[cfg(debug_assertions)] // println!("info: ({type_:?}), {message}"); } else { // #[cfg(debug_assertions)] // println!("note: ({type_:?}), {message}"); } vk::FALSE }