#![deny(unsafe_code)] use crate::error::*; use crate::graphics::{ Permanent, ForReinit }; use crate::graphics::permanent::{ QueueFamilyIndices, EnableAnisotropy }; use crate::graphics::util::{ allocate_buffer, allocate_image, init_image_view }; use crate::shader_data::UniformBlock; use std::collections::BTreeSet; use std::mem::size_of; use vulkanalia::{ Device, Instance }; use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0, KhrSwapchainExtensionDeviceCommands }; use winit::window::Window; // TODO: use VK_KHR_swapchain_maintenance1 to put a fence on the presentation // operation. doing that will remove the requirement that we have more // simultaneous frames than images. pub const N_SIMULTANEOUS_FRAMES: usize = 5; // The WindowDressing collects the Vulkan graphics objects which need to be // regenerated or modified when the window changes in certain ways, such as // resizing, but are not needed during rendering. The ones which don't need to // be regenerated are collected in Permanent. The ones which are needed during // rendering are collected in RenderState, below. #[derive(Debug)] pub struct WindowDressing { pub swapchain: Swapchain, color_image: vk::Image, color_image_memory: vk::DeviceMemory, pub color_image_view: vk::ImageView, depth_image: vk::Image, depth_image_memory: vk::DeviceMemory, pub depth_image_view: vk::ImageView, pub depth_format: vk::Format, pub sampler: vk::Sampler, pub uniform_buffers: Vec, pub uniform_buffer_memory: Vec, pub descriptor_pool: vk::DescriptorPool, pub concurrency: Concurrency, } // A swapchain is the generalized facility that is used to implement // double buffering, triple buffering, rendering passes that feed into each // other, and other things of that nature. It's a first-class thing but for // now, we use at most one of it. We also support running without one. #[derive(Debug)] pub struct Swapchain { pub swapchain: vk::SwapchainKHR, pub images: Vec, pub image_views: Vec, pub format: vk::Format, pub extent: vk::Extent2D, } #[derive(Debug)] pub struct Concurrency { pub image_available_semaphores: Vec, pub rendering_finished_semaphores: Vec, // Okay, the lifetime management on the fences is really subtle. There is // one fence for each frame, and frame_fences holds the authoritative // reference to it. // // There is one entry in image_fences for each image. The number of images // is not directly related to the number of frames; it will likely be // larger, but may be smaller or the same. At the start of execution, the // entries are all nulls. Each time an image is acquired from the swapchain, // the corresponding entry in image_fences is overwritten with a duplicate // of the frame fence. This happens during rendering of the frame, so the // frame fence is in the "signaled" state. It will be reset right before // submitting the queue, then signaled again when the submission completes. pub frame_fences: Vec, pub image_fences: Vec, } impl WindowDressing { pub fn new(permanent: &Permanent, for_reinit: &ForReinit, enable_anisotropy: EnableAnisotropy, mip_count: u32) -> Result { let window = &permanent.window; let instance = &permanent.instance; let surface = &permanent.surface; let device = &permanent.device; let sample_count = for_reinit.sample_count; let indices = &for_reinit.indices; let swapchain = init_swapchain( window, instance, surface, device, &indices)?; let (color_image, color_image_memory, color_image_view) = init_color(instance, device, &swapchain.extent, sample_count, swapchain.format)?; let (depth_image, depth_image_memory, depth_image_view, depth_format) = init_depth(instance, device, &swapchain.extent, sample_count)?; let sampler = init_sampler(&device, &enable_anisotropy, mip_count)?; let (uniform_buffers, uniform_buffer_memory) = init_uniform_buffers(instance, device, swapchain.images.len())?; let descriptor_pool = init_descriptor_pool(device, swapchain.images.len())?; let concurrency = init_concurrency(device, &swapchain.images)?; Ok(WindowDressing { swapchain, color_image, color_image_memory, color_image_view, depth_image, depth_image_memory, depth_image_view, depth_format, sampler, uniform_buffers, uniform_buffer_memory, descriptor_pool, concurrency, }) } #[allow(unsafe_code)] pub fn reinit(&mut self, permanent: &Permanent, for_reinit: &ForReinit) -> Result<()> { let window = &permanent.window; let instance = &permanent.instance; let surface = &permanent.surface; let device = &permanent.device; let sample_count = for_reinit.sample_count; let indices = &for_reinit.indices; unsafe { device.device_wait_idle() }.unwrap(); self.destroy_replaceable(device); let swapchain = init_swapchain(window, instance, surface, device, &indices)?; let (color_image, color_image_memory, color_image_view) = init_color(instance, device, &swapchain.extent, sample_count, swapchain.format)?; let (depth_image, depth_image_memory, depth_image_view, depth_format) = init_depth(instance, device, &swapchain.extent, sample_count)?; let (uniform_buffers, uniform_buffer_memory) = init_uniform_buffers(instance, device, swapchain.images.len())?; // Notice that we did NOT reuse the descriptor pool. let descriptor_pool = init_descriptor_pool(device, swapchain.images.len())?; self.concurrency.image_fences.resize(swapchain.images.len(), vk::Fence::null()); self.swapchain = swapchain; self.color_image = color_image; self.color_image_memory = color_image_memory; self.color_image_view = color_image_view; self.depth_image = depth_image; self.depth_image_memory = depth_image_memory; self.depth_image_view = depth_image_view; self.depth_format = depth_format; self.uniform_buffers = uniform_buffers; self.uniform_buffer_memory = uniform_buffer_memory; self.descriptor_pool = descriptor_pool; Ok(()) } // This relies on its caller to have already waited for the device to be // idle. #[allow(unsafe_code)] pub fn destroy(mut self, device: &Device) { self.destroy_replaceable(device); unsafe { device.destroy_sampler(self.sampler, None) }; for semaphore in self.concurrency.image_available_semaphores { unsafe { device.destroy_semaphore(semaphore, None) }; } for semaphore in self.concurrency.rendering_finished_semaphores { unsafe { device.destroy_semaphore(semaphore, None) }; } for fence in self.concurrency.frame_fences { unsafe { device.destroy_fence(fence, None) }; } } #[allow(unsafe_code)] fn destroy_replaceable(&mut self, device: &Device) { // While the descriptor pool is also a pool, it has a preallocated size // which will be different next time. So, we destroy it all the way. unsafe { device.destroy_descriptor_pool(self.descriptor_pool, None) }; // Notice that, unlike the vertex and index buffers, we destroy and // re-create these on every reinitialization. That's because the number of // them depends on how many images the swapchain has. for buffer in &self.uniform_buffers { unsafe { device.destroy_buffer(*buffer, None) }; } for memory in &self.uniform_buffer_memory { unsafe { device.free_memory(*memory, None) }; } unsafe { device.destroy_image(self.color_image, None) }; unsafe { device.free_memory(self.color_image_memory, None) }; unsafe { device.destroy_image_view(self.color_image_view, None) }; unsafe { device.destroy_image(self.depth_image, None) }; unsafe { device.free_memory(self.depth_image_memory, None) }; unsafe { device.destroy_image_view(self.depth_image_view, None) }; for view in &self.swapchain.image_views { unsafe { device.destroy_image_view(*view, None) }; } unsafe { device.destroy_swapchain_khr(self.swapchain.swapchain, None) }; } } #[allow(unsafe_code)] fn init_swapchain(window: &Window, instance: &Instance, surface: &vk::SurfaceKHR, device: &Device, indices: &QueueFamilyIndices) -> Result { let physical_device = device.physical_device(); let (capabilities, formats, presentation_modes) = Permanent::find_device_swapchain_features( instance, surface, &physical_device)?.require()?; let format = pick_surface_format(&formats)?; let presentation_mode = pick_presentation_mode(&presentation_modes)?; let extent = pick_image_extent(window, capabilities)?; let mut image_count = capabilities.min_image_count + 1; if capabilities.max_image_count != 0 { image_count = image_count.clamp(0, capabilities.max_image_count); } let mut unique_queue_family_indices = BTreeSet::new(); unique_queue_family_indices.insert(indices.graphics); unique_queue_family_indices.insert(indices.presentation); // If there's only one queue, we use exclusive sharing mode, which // will allow things to work without locks. Otherwise we use concurrent // mode. let (ordered_indices, sharing_mode) = if unique_queue_family_indices.len() < 2 { (vec![indices.graphics], vk::SharingMode::EXCLUSIVE) } else { (vec![indices.graphics, indices.presentation], vk::SharingMode::CONCURRENT) }; let swapchain_info = vk::SwapchainCreateInfoKHR::builder() .surface(*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(sharing_mode) .queue_family_indices(&ordered_indices) .pre_transform(capabilities.current_transform) .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) .present_mode(presentation_mode) .clipped(true) .old_swapchain(vk::SwapchainKHR::null()); let swapchain = unsafe { device.create_swapchain_khr(&swapchain_info, None) }?; let images = unsafe { device.get_swapchain_images_khr(swapchain) }?; let mut image_views = Vec::new(); for image in &images { let view = init_image_view(device, image, 1, format.format, vk::ImageAspectFlags::COLOR)?; image_views.push(view); } Ok(Swapchain { swapchain, images, image_views, format: format.format, extent }) } #[allow(unsafe_code)] fn init_color(instance: &Instance, device: &Device, extent: &vk::Extent2D, sample_count: vk::SampleCountFlags, format: vk::Format) -> Result<(vk::Image, vk::DeviceMemory, vk::ImageView)> { let (image, image_memory) = allocate_image(instance, device, extent.width, extent.height, 1, sample_count, format, vk::ImageTiling::OPTIMAL, vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT, vk::MemoryPropertyFlags::DEVICE_LOCAL)?; let image_view = init_image_view(device, &image, 1, format, vk::ImageAspectFlags::COLOR)?; Ok((image, image_memory, image_view)) } #[allow(unsafe_code)] fn init_depth(instance: &Instance, device: &Device, extent: &vk::Extent2D, sample_count: vk::SampleCountFlags) -> Result<(vk::Image, vk::DeviceMemory, vk::ImageView, vk::Format)> { let physical_device = device.physical_device(); let format = pick_depth_format(instance, &physical_device)?; let (image, image_memory) = allocate_image(instance, device, extent.width, extent.height, 1, sample_count, format, vk::ImageTiling::OPTIMAL, vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, vk::MemoryPropertyFlags::DEVICE_LOCAL)?; let image_view = init_image_view(device, &image, 1, format, vk::ImageAspectFlags::DEPTH)?; Ok((image, image_memory, image_view, format)) } fn init_uniform_buffers(instance: &Instance, device: &Device, count: usize) -> Result<(Vec, Vec)> { let mut buffers = Vec::new(); let mut all_memory = Vec::new(); for _ in 0 .. count { let (buffer, memory) = allocate_buffer( instance, device, size_of::>() as vk::DeviceSize, vk::BufferUsageFlags::UNIFORM_BUFFER, vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE)?; buffers.push(buffer); all_memory.push(memory); } Ok((buffers, all_memory)) } #[allow(unsafe_code)] fn init_sampler(device: &Device, enable_anisotropy: &EnableAnisotropy, mip_count: u32) -> Result { let mut sampler_info = vk::SamplerCreateInfo::builder() .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) .border_color(vk::BorderColor::INT_OPAQUE_BLACK) .unnormalized_coordinates(false) .compare_enable(false) .compare_op(vk::CompareOp::ALWAYS) .mipmap_mode(vk::SamplerMipmapMode::LINEAR) .mip_lod_bias(0.0) .min_lod(0.0) .max_lod(mip_count as f32); sampler_info = if enable_anisotropy.0 { sampler_info.anisotropy_enable(true) .max_anisotropy(16.0) } else { sampler_info.anisotropy_enable(false) .max_anisotropy(1.0) }; let sampler = unsafe { device.create_sampler(&sampler_info, None) }?; Ok(sampler) } #[allow(unsafe_code)] fn init_descriptor_pool(device: &Device, count: usize) -> Result { let uniform_block_size = vk::DescriptorPoolSize::builder() .type_(vk::DescriptorType::UNIFORM_BUFFER) .descriptor_count(count as u32); let sampler_size = vk::DescriptorPoolSize::builder() .type_(vk::DescriptorType::SAMPLER) .descriptor_count(count as u32); let texture_size = vk::DescriptorPoolSize::builder() .type_(vk::DescriptorType::SAMPLED_IMAGE) .descriptor_count(count as u32); let sizes = [uniform_block_size, sampler_size, texture_size]; let pool_info = vk::DescriptorPoolCreateInfo::builder() .pool_sizes(&sizes) .max_sets(count as u32); let pool = unsafe { device.create_descriptor_pool(&pool_info, None) }?; Ok(pool) } #[allow(unsafe_code)] fn init_concurrency(device: &Device, swapchain_images: &Vec) -> Result { let semaphore_info = vk::SemaphoreCreateInfo::builder(); let fence_info = vk::FenceCreateInfo::builder() .flags(vk::FenceCreateFlags::SIGNALED); let mut image_available_semaphores = Vec::new(); let mut rendering_finished_semaphores = Vec::new(); let mut frame_fences = Vec::new(); for _ in 0 .. N_SIMULTANEOUS_FRAMES { image_available_semaphores.push(unsafe { device.create_semaphore(&semaphore_info, None) }?); rendering_finished_semaphores.push(unsafe { device.create_semaphore(&semaphore_info, None) }?); frame_fences.push(unsafe { device.create_fence(&fence_info, None) }?); } let mut image_fences = Vec::new(); for _ in 0 .. swapchain_images.len() { image_fences.push(vk::Fence::null()); } Ok(Concurrency { image_available_semaphores, rendering_finished_semaphores, frame_fences, image_fences: image_fences, }) } fn pick_surface_format(available_formats: &Vec) -> Result { for format in available_formats { if format.format == vk::Format::B8G8R8A8_SRGB && format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR { return Ok(format.clone()); } } return Ok(available_formats[0].clone()); } #[allow(unsafe_code)] fn pick_depth_format(instance: &Instance, physical_device: &vk::PhysicalDevice) -> Result { let required_features = vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT; for format in [vk::Format::D32_SFLOAT, vk::Format::D32_SFLOAT_S8_UINT, vk::Format::D24_UNORM_S8_UINT] { let properties = unsafe { instance.get_physical_device_format_properties( *physical_device, format) }; if properties.optimal_tiling_features.contains(required_features) { return Ok(format); } } Err(Error { message: "There is no supported depth-buffer sample format.".to_string() }) } fn pick_presentation_mode(_available_modes: &Vec) -> Result { // It's guaranteed to have this one. return Ok(vk::PresentModeKHR::FIFO); } fn pick_image_extent(window: &Window, capabilities: vk::SurfaceCapabilitiesKHR) -> Result { if capabilities.current_extent.width != u32::MAX && capabilities.current_extent.height != u32::MAX { Ok(capabilities.current_extent) } else { let window_size = window.inner_size(); let width = window_size.width .clamp(capabilities.min_image_extent.width, capabilities.max_image_extent.width); let height = window_size.height .clamp(capabilities.min_image_extent.height, capabilities.max_image_extent.height); Ok(vk::Extent2D::builder().width(width).height(height).build()) } }