#![deny(unsafe_code)] use crate::error::*; use crate::graphics::{ Permanent, ForReinit, WindowDressing, Texture }; use crate::graphics::util::allocate_buffer; use crate::shader_data::UniformBlock; use std::mem::size_of; use vulkanalia::{ Device, Instance }; use vulkanalia::vk::{ self, HasBuilder, DeviceV1_0 }; // Frame is a state object that collects the Vulkan graphics objects which // are used as part of rendering and which need to exist in multiples, one for // each frame that can be rendered in parallel (see N_SIMULTANEOUS_FRAMES in // window_dressing.rs). As with WindowDressing, these need to be regenerated // or modified when the window changes. #[derive(Debug)] pub struct Frame { pub framebuffer: vk::Framebuffer, pub command_buffer: vk::CommandBuffer, // Uniform buffers are unlike the other things in Frame in that they are // not used at render-time. It's still cleanest to have them here, since // they exist per-frame. Otherwise we get action-at-a-distance passing // around indices, which is harder to trace through. pub uniform_buffer: vk::Buffer, pub uniform_buffer_memory: vk::DeviceMemory, pub descriptor_set: vk::DescriptorSet, } impl Frame { // The lifecycle stuff for Frame is a little different. The Vulkan API to // allocate and deallocate command buffers is designed on the assumption you // want to handle a few of them simultaneously. That is in fact what we want, // so the interfaces to new() and reinit() work on Vec instead of on // an indidivual Frame. #[allow(unsafe_code)] pub fn new(permanent: &Permanent, for_reinit: &ForReinit, window_dressing: &WindowDressing, texture: &Texture, render_pass: &vk::RenderPass) -> Result> { let mut frames = Vec::new(); Frame::reinit(&mut frames, permanent, for_reinit, window_dressing, texture, render_pass)?; Ok(frames) } // See new() in regard to the Vec. #[allow(unsafe_code)] pub fn reinit(frames: &mut Vec, permanent: &Permanent, for_reinit: &ForReinit, window_dressing: &WindowDressing, texture: &Texture, render_pass: &vk::RenderPass) -> Result<()> { Frame::destroy_replaceable(frames, permanent); let instance = &permanent.instance; let device = &permanent.device; let primary_command_pool = &permanent.primary_command_pool; let descriptor_set_layout = &for_reinit.descriptor_set_layout; let swapchain = &window_dressing.swapchain; let color_image_view = &window_dressing.color_image_view; let depth_image_view = &window_dressing.depth_image_view; let descriptor_pool = &window_dressing.descriptor_pool; let sampler = &window_dressing.sampler; frames.clear(); let count = swapchain.image_views.len(); // Notice that we reused the command pool. let command_buffers = allocate_command_buffers(count, device, primary_command_pool)?; let descriptor_sets = allocate_descriptor_sets( count, device, descriptor_set_layout, descriptor_pool)?; for (index, color_resolve_image_view) in swapchain.image_views.iter().enumerate() { let command_buffer = command_buffers[index]; let mut descriptor_set = descriptor_sets[index]; let framebuffer = init_framebuffer( device, &swapchain.extent, &color_image_view, &depth_image_view, color_resolve_image_view, &render_pass)?; let (uniform_buffer, uniform_buffer_memory) = init_uniform_buffer(instance, device)?; configure_descriptor_set(&mut descriptor_set, device, &uniform_buffer, &texture.image_view, sampler)?; frames.push(Frame { framebuffer, command_buffer, uniform_buffer, uniform_buffer_memory, descriptor_set, }); } Ok(()) } // This relies on its caller to have already waited for the device to be // idle. #[allow(unsafe_code)] pub fn destroy(frames: &mut Vec, permanent: &Permanent) { Frame::destroy_replaceable(frames, permanent); } #[allow(unsafe_code)] pub fn destroy_replaceable(frames: &mut Vec, permanent: &Permanent) { if frames.is_empty() { return; } let device = &permanent.device; let mut command_buffers = Vec::new(); for frame in frames { unsafe { device.destroy_framebuffer(frame.framebuffer, None) }; command_buffers.push(frame.command_buffer); unsafe { device.destroy_buffer(frame.uniform_buffer, None) }; unsafe { device.free_memory(frame.uniform_buffer_memory, None) }; } // Notice that we free the buffers in the pool, but do not destroy the // pool itself. Notice also that we only do this for the primary command // pool, because that's the only one where we've kept track of the // buffers. We promise ourselves to free buffers in the transient pool // immediately after using them. unsafe { device.free_command_buffers(permanent.primary_command_pool, &command_buffers) }; } } #[allow(unsafe_code)] fn init_framebuffer(device: &Device, extent: &vk::Extent2D, color_image_view: &vk::ImageView, depth_image_view: &vk::ImageView, color_resolve_image_view: &vk::ImageView, render_pass: &vk::RenderPass) -> Result { let attachments = [*color_image_view, *depth_image_view, *color_resolve_image_view]; let framebuffer_info = vk::FramebufferCreateInfo::builder() .render_pass(*render_pass) .attachments(&attachments) .width(extent.width) .height(extent.height) .layers(1); let framebuffer = unsafe { device.create_framebuffer(&framebuffer_info, None) }?; Ok(framebuffer) } #[allow(unsafe_code)] fn allocate_command_buffers(count: usize, device: &Device, command_pool: &vk::CommandPool) -> Result> { let command_buffer_allocation_info = vk::CommandBufferAllocateInfo::builder() .command_pool(*command_pool) .level(vk::CommandBufferLevel::PRIMARY) .command_buffer_count(count as u32); let command_buffers = unsafe { device.allocate_command_buffers(&command_buffer_allocation_info) }?; Ok(command_buffers) } #[allow(unsafe_code)] fn allocate_descriptor_sets(count: usize, device: &Device, descriptor_set_layout: &vk::DescriptorSetLayout, descriptor_pool: &vk::DescriptorPool) -> Result> { let layouts = vec![*descriptor_set_layout; count]; let descriptor_set_info = vk::DescriptorSetAllocateInfo::builder() .descriptor_pool(*descriptor_pool) .set_layouts(&layouts[..]); let descriptor_sets = unsafe { device.allocate_descriptor_sets(&descriptor_set_info) }?; Ok(descriptor_sets) } #[allow(unsafe_code)] fn configure_descriptor_set(descriptor_set: &mut vk::DescriptorSet, device: &Device, uniform_buffer: &vk::Buffer, texture_image_view: &vk::ImageView, sampler: &vk::Sampler) -> Result<()> { let buffer_info = vk::DescriptorBufferInfo::builder() .buffer(*uniform_buffer) .offset(0) .range(size_of::>() as vk::DeviceSize); let buffer_info_list = [buffer_info]; let uniform_block_write_info = vk::WriteDescriptorSet::builder() .dst_set(*descriptor_set) .dst_binding(0) .dst_array_element(0) .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) .buffer_info(&buffer_info_list); let sampler_image_info = vk::DescriptorImageInfo::builder() .sampler(*sampler); let sampler_image_info_list = [sampler_image_info]; let sampler_write_info = vk::WriteDescriptorSet::builder() .dst_set(*descriptor_set) .dst_binding(1) .dst_array_element(0) .descriptor_type(vk::DescriptorType::SAMPLER) .image_info(&sampler_image_info_list); let texture_image_info = vk::DescriptorImageInfo::builder() .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) .image_view(*texture_image_view); let texture_image_info_list = [texture_image_info]; let texture_write_info = vk::WriteDescriptorSet::builder() .dst_set(*descriptor_set) .dst_binding(2) .dst_array_element(0) .descriptor_type(vk::DescriptorType::SAMPLED_IMAGE) .image_info(&texture_image_info_list); let write_info_list = [ uniform_block_write_info, sampler_write_info, texture_write_info ]; let copy_info_list: [vk::CopyDescriptorSet; 0] = []; unsafe { device.update_descriptor_sets(&write_info_list, ©_info_list) }; Ok(()) } fn init_uniform_buffer(instance: &Instance, device: &Device) -> Result<(vk::Buffer, vk::DeviceMemory)> { let (buffer, memory) = allocate_buffer( instance, device, size_of::>() as vk::DeviceSize, vk::BufferUsageFlags::UNIFORM_BUFFER, vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE)?; Ok((buffer, memory)) }