use super::*; use crate::VALIDATION_ENABLED; use ::anyhow::{anyhow, Result}; use ::log::*; use ::std::collections::HashSet; use ::std::ffi::CStr; use ::std::os::raw::c_void; use ::vulkanalia::prelude::v1_0::*; use ::vulkanalia::window as vk_window; use ::winit::window::Window; 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 { error!("({:?}) {}", type_, message); } else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING { warn!("({:?}) {}", type_, message); } else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::INFO { debug!("({:?}) {}", type_, message); } else { trace!("({:?}) {}", type_, message); } vk::FALSE } //================================================ // Physical Device //================================================ pub(super) unsafe fn create_instance( window: &Window, entry: &Entry, data: &mut AppData, ) -> Result { // Application Info let application_info = vk::ApplicationInfo::builder() .application_name(b"Vulkan Tutorial\0") .application_version(vk::make_version(1, 0, 0)) .engine_name(b"No Engine\0") .engine_version(vk::make_version(1, 0, 0)) .api_version(vk::make_version(1, 0, 0)); // Layers let available_layers = entry .enumerate_instance_layer_properties()? .iter() .map(|l| l.layer_name) .collect::>(); if VALIDATION_ENABLED && !available_layers.contains(&VALIDATION_LAYER) { return Err(anyhow!("Validation layer requested but not supported.")); } let layers = if VALIDATION_ENABLED { vec![VALIDATION_LAYER.as_ptr()] } else { Vec::new() }; // Extensions let mut extensions = vk_window::get_required_instance_extensions(window) .iter() .map(|e| e.as_ptr()) .collect::>(); if VALIDATION_ENABLED { extensions.push(vk::EXT_DEBUG_UTILS_EXTENSION.name.as_ptr()); } // Compatibility extension for macOS let flags = if entry .enumerate_instance_extension_properties(None)? .iter() .any(|e| e.extension_name == vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name) { extensions.push(vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name.as_ptr()); extensions.push( vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION .name .as_ptr(), ); vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR } else { vk::InstanceCreateFlags::empty() }; // Create let mut info = vk::InstanceCreateInfo::builder() .application_info(&application_info) .enabled_layer_names(&layers) .enabled_extension_names(&extensions) .flags(flags); let mut debug_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() .message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all()) .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) .user_callback(Some(debug_callback)); if VALIDATION_ENABLED { info = info.push_next(&mut debug_info); } let instance = entry.create_instance(&info, None)?; // Messenger if VALIDATION_ENABLED { data.messenger = instance.create_debug_utils_messenger_ext(&debug_info, None)?; } Ok(instance) } pub(super) unsafe fn pick_physical_device(instance: &Instance, data: &mut AppData) -> Result<()> { for physical_device in instance.enumerate_physical_devices()? { let properties = instance.get_physical_device_properties(physical_device); if let Err(error) = check_physical_device(instance, data, physical_device) { warn!( "Skipping physical device (`{}`): {}", properties.device_name, error ); } else { info!("Selected physical device (`{}`).", properties.device_name); data.physical_device = physical_device; return Ok(()); } } Err(anyhow!("Failed to find suitable physical device.")) } unsafe fn check_physical_device( instance: &Instance, data: &AppData, physical_device: vk::PhysicalDevice, ) -> Result<()> { 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.present_modes.is_empty() { return Err(anyhow!(SuitabilityError("Insufficient swapchain support."))); } Ok(()) } unsafe fn check_physical_device_extensions( instance: &Instance, physical_device: vk::PhysicalDevice, ) -> Result<()> { let extensions = instance .enumerate_device_extension_properties(physical_device, None)? .iter() .map(|e| e.extension_name) .collect::>(); if DEVICE_EXTENSIONS.iter().all(|e| extensions.contains(e)) { Ok(()) } else { Err(anyhow!(SuitabilityError( "Missing required device extensions." ))) } } //================================================ // Logical Device //================================================ pub(super) unsafe fn create_logical_device( instance: &Instance, data: &mut AppData, ) -> Result { // Queue Create Infos let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?; let mut unique_indices = HashSet::new(); unique_indices.insert(indices.graphics); unique_indices.insert(indices.present); let queue_priorities = &[1.0]; let queue_infos = unique_indices .iter() .map(|i| { vk::DeviceQueueCreateInfo::builder() .queue_family_index(*i) .queue_priorities(queue_priorities) }) .collect::>(); // Layers let layers = if VALIDATION_ENABLED { vec![VALIDATION_LAYER.as_ptr()] } else { vec![] }; // Extensions let mut extensions = DEVICE_EXTENSIONS .iter() .map(|n| n.as_ptr()) .collect::>(); // mac OS Metal -> Vulkan rendering fix if instance .enumerate_device_extension_properties(data.physical_device, None)? .iter() .any(|e| e.extension_name == vk::KHR_PORTABILITY_SUBSET_EXTENSION.name) { extensions.push(vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr()); } // Features let features = vk::PhysicalDeviceFeatures::builder(); // Create 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)?; // Queues data.graphics_queue = device.get_device_queue(indices.graphics, 0); data.present_queue = device.get_device_queue(indices.present, 0); Ok(device) } //================================================ // Swapchain //================================================ pub(super) unsafe fn create_swapchain( window: &Window, instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<()> { let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?; let support = SwapchainSupport::get(instance, data, data.physical_device)?; let surface_format = get_swapchain_surface_format(&support.formats); let present_mode = get_swapchain_present_mode(&support.present_modes); let extent = get_swapchain_extent(window, support.capabilities); let mut image_count = support.capabilities.min_image_count + 1; if support.capabilities.max_image_count != 0 && image_count > support.capabilities.max_image_count { image_count = support.capabilities.max_image_count; } let mut queue_family_indices = vec![]; let image_sharing_mode = if indices.graphics != indices.present { queue_family_indices.push(indices.graphics); queue_family_indices.push(indices.present); vk::SharingMode::CONCURRENT } else { vk::SharingMode::EXCLUSIVE }; let info = vk::SwapchainCreateInfoKHR::builder() .surface(data.surface) .min_image_count(image_count) .image_format(surface_format.format) .image_color_space(surface_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(present_mode) .clipped(true) .old_swapchain(vk::SwapchainKHR::null()); data.swapchain = device.create_swapchain_khr(&info, None)?; data.swapchain_images = device.get_swapchain_images_khr(data.swapchain)?; data.swapchain_format = surface_format.format; data.swapchain_extent = extent; Ok(()) } fn get_swapchain_surface_format(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR { formats .iter() .cloned() .find(|f| { f.format == vk::Format::B8G8R8A8_SRGB && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR }) .unwrap_or_else(|| formats[0]) } fn get_swapchain_present_mode(present_modes: &[vk::PresentModeKHR]) -> vk::PresentModeKHR { present_modes .iter() .cloned() .find(|m| *m == vk::PresentModeKHR::MAILBOX) .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 size = window.inner_size(); let clamp = |min: u32, max: u32, v: u32| min.max(max.min(v)); vk::Extent2D::builder() .width(clamp( capabilities.min_image_extent.width, capabilities.max_image_extent.width, size.width, )) .height(clamp( capabilities.min_image_extent.height, capabilities.max_image_extent.height, size.height, )) .build() } } pub(super) unsafe fn create_swapchain_image_views( device: &Device, data: &mut AppData, ) -> Result<()> { data.swapchain_image_views = data .swapchain_images .iter() .map(|i| { 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(*i) .view_type(vk::ImageViewType::_2D) .format(data.swapchain_format) .components(components) .subresource_range(subresource_range); device.create_image_view(&info, None) }) .collect::, _>>()?; Ok(()) } //================================================ // Pipeline //================================================ pub(super) unsafe fn create_render_pass( instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<()> { 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); // Subpasses let color_attachment_ref = vk::AttachmentReference::builder() .attachment(0) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let color_attachments = &[color_attachment_ref]; let subpass = vk::SubpassDescription::builder() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .color_attachments(color_attachments); let attachments = &[color_attachment]; let subpasses = &[subpass]; let info = vk::RenderPassCreateInfo::builder() .attachments(attachments) .subpasses(subpasses); data.render_pass = device.create_render_pass(&info, None)?; Ok(()) } pub(super) unsafe fn create_pipeline(device: &Device, data: &mut AppData) -> Result<()> { let vert = include_bytes!("../../shaders/vert.spv"); let frag = include_bytes!("../../shaders/frag.spv"); let vert_shader_module = create_shader_module(device, &vert[..])?; let frag_shader_module = create_shader_module(device, &frag[..])?; let vert_stage = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::VERTEX) .module(vert_shader_module) .name(b"main\0"); let frag_stage = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::FRAGMENT) .module(frag_shader_module) .name(b"main\0"); 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(0.0) .y(0.0) .width(data.swapchain_extent.width as f32) .height(data.swapchain_extent.height as f32) .min_depth(0.0) .max_depth(1.0); let scissor = vk::Rect2D::builder() .offset(vk::Offset2D { x: 0, y: 0 }) .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(1.0) .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 = vk::PipelineColorBlendAttachmentState::builder() .color_write_mask(vk::ColorComponentFlags::all()) .blend_enable(false) .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 attachments = &[attachment]; let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder() .logic_op_enable(false) .logic_op(vk::LogicOp::COPY) .attachments(attachments) .blend_constants([0.0, 0.0, 0.0, 0.0]); let layout_info = vk::PipelineLayoutCreateInfo::builder(); data.pipeline_layout = device.create_pipeline_layout(&layout_info, None)?; let stages = &[vert_stage, frag_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; // Cleanup device.destroy_shader_module(vert_shader_module, None); device.destroy_shader_module(frag_shader_module, None); Ok(()) } unsafe fn create_shader_module(device: &Device, bytecode: &[u8]) -> Result { let bytecode = Vec::::from(bytecode); let (prefix, code, suffix) = bytecode.align_to::(); if !prefix.is_empty() || !suffix.is_empty() { return Err(anyhow!("Shader bytecode is not properly aligned.")); } let info = vk::ShaderModuleCreateInfo::builder() .code_size(bytecode.len()) .code(code); Ok(device.create_shader_module(&info, None)?) } pub(super) unsafe fn create_framebuffers(device: &Device, data: &mut AppData) -> Result<()> { data.framebuffers = data .swapchain_image_views .iter() .map(|i| { let attachments = &[*i]; let create_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(&create_info, None) }) .collect::, _>>()?; Ok(()) } //================================================ // Command Pool //================================================ pub(super) unsafe fn create_command_pool( instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<()> { 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)?; Ok(()) } //================================================ // Command Buffers //================================================ pub(super) unsafe fn create_command_buffers(device: &Device, data: &mut AppData) -> Result<()> { // Create the buffers 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)?; // Add commands for (i, command_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(*command_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.0, 0.0, 0.0, 1.0], }, }; 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(*command_buffer, &info, vk::SubpassContents::INLINE); device.cmd_bind_pipeline( *command_buffer, vk::PipelineBindPoint::GRAPHICS, data.pipeline, ); device.cmd_draw(*command_buffer, 3, 1, 0, 0); device.cmd_end_render_pass(*command_buffer); device.end_command_buffer(*command_buffer)?; } Ok(()) }