#![deny(unsafe_code)] use crate::error::*; use crate::graphics_permanent::{ PermanentGraphicsState, GraphicsStateForReinit, QueueFamilyIndices }; use crate::shader_data::{ VERTICES, INDICES, Vertex, UniformBlock }; use std::collections::BTreeSet; use std::io::Cursor; use std::ptr::copy_nonoverlapping; use png::Decoder; 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. The ones which don't need that are collected above, in // PermanentGraphicsState. #[derive(Debug)] pub struct WindowDressing { pub swapchain: Swapchain, render_pass: vk::RenderPass, pipeline: vk::Pipeline, pipeline_layout: vk::PipelineLayout, framebuffers: Vec, primary_command_pool: vk::CommandPool, transient_command_pool: vk::CommandPool, vertex_buffer: vk::Buffer, vertex_buffer_memory: vk::DeviceMemory, index_buffer: vk::Buffer, index_buffer_memory: vk::DeviceMemory, texture_image: vk::Image, texture_image_memory: vk::DeviceMemory, texture_image_view: vk::ImageView, uniform_buffers: Vec, pub uniform_buffer_memory: Vec, descriptor_pool: vk::DescriptorPool, descriptor_sets: Vec, pub command_buffers: Vec, 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, images: Vec, image_views: Vec, 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: &PermanentGraphicsState, for_reinit: &GraphicsStateForReinit) -> Result { let window = &permanent.window; let instance = &permanent.instance; let surface = &permanent.surface; let device = &permanent.device; let sampler = &permanent.sampler; let graphics_queue = &permanent.graphics_queue; let physical_device = &for_reinit.physical_device; let indices = &for_reinit.indices; let descriptor_set_layout = &for_reinit.descriptor_set_layout; let swapchain = init_swapchain( window, instance, surface, &physical_device, device, &indices)?; let render_pass = init_render_pass(device, &swapchain.format)?; let (pipeline_layout, pipeline) = init_pipeline(device, descriptor_set_layout, &swapchain.extent, &render_pass)?; let framebuffers = init_framebuffers( device, &swapchain.extent, &swapchain.image_views, &render_pass)?; let (primary_command_pool, transient_command_pool) = init_command_pools(device, indices)?; let (vertex_buffer, vertex_buffer_memory) = init_vertex_buffer(instance, physical_device, device, graphics_queue, &transient_command_pool)?; let (index_buffer, index_buffer_memory) = init_index_buffer(instance, physical_device, device, graphics_queue, &transient_command_pool)?; let (texture_image, texture_image_memory, texture_image_view) = init_texture(instance, physical_device, device, graphics_queue, &transient_command_pool)?; let (uniform_buffers, uniform_buffer_memory) = init_uniform_buffers(instance, physical_device, device, swapchain.images.len())?; let descriptor_pool = init_descriptor_pool(device, swapchain.images.len())?; let descriptor_sets = init_descriptor_sets(device, descriptor_set_layout, &uniform_buffers, &descriptor_pool, swapchain.images.len(), &texture_image_view, sampler)?; let command_buffers = init_commands( device, &swapchain.extent, &framebuffers, &render_pass, &pipeline_layout, &pipeline, &vertex_buffer, &index_buffer, &descriptor_sets, &primary_command_pool)?; let concurrency = init_concurrency(device, &swapchain.images)?; Ok(WindowDressing { swapchain, render_pass, pipeline, pipeline_layout, framebuffers, vertex_buffer, vertex_buffer_memory, index_buffer, index_buffer_memory, texture_image, texture_image_memory, texture_image_view, uniform_buffers, uniform_buffer_memory, descriptor_pool, descriptor_sets, primary_command_pool, transient_command_pool, command_buffers, concurrency, }) } #[allow(unsafe_code)] pub fn reinit(&mut self, permanent: &PermanentGraphicsState, for_reinit: &GraphicsStateForReinit) -> Result<()> { let window = &permanent.window; let instance = &permanent.instance; let surface = &permanent.surface; let device = &permanent.device; let sampler = &permanent.sampler; let physical_device = &for_reinit.physical_device; let indices = &for_reinit.indices; let descriptor_set_layout = &for_reinit.descriptor_set_layout; unsafe { device.device_wait_idle() }.unwrap(); self.destroy_replaceable(device); let swapchain = init_swapchain( window, instance, surface, &physical_device, device, &indices)?; let render_pass = init_render_pass(device, &swapchain.format)?; let (pipeline_layout, pipeline) = init_pipeline(device, descriptor_set_layout, &swapchain.extent, &render_pass)?; let framebuffers = init_framebuffers( device, &swapchain.extent, &swapchain.image_views, &render_pass)?; let (uniform_buffers, uniform_buffer_memory) = init_uniform_buffers(instance, physical_device, device, swapchain.images.len())?; // Notice that we did NOT reuse the descriptor pool. let descriptor_pool = init_descriptor_pool(device, swapchain.images.len())?; let descriptor_sets = init_descriptor_sets(device, descriptor_set_layout, &uniform_buffers, &descriptor_pool, swapchain.images.len(), &self.texture_image_view, sampler)?; // Notice that we reused the command pool. let command_buffers = init_commands( device, &swapchain.extent, &framebuffers, &render_pass, &pipeline_layout, &pipeline, &self.vertex_buffer, &self.index_buffer, &descriptor_sets, &self.primary_command_pool)?; self.concurrency.image_fences.resize(swapchain.images.len(), vk::Fence::null()); self.swapchain = swapchain; self.render_pass = render_pass; self.pipeline = pipeline; self.pipeline_layout = pipeline_layout; self.framebuffers = framebuffers; self.uniform_buffers = uniform_buffers; self.uniform_buffer_memory = uniform_buffer_memory; self.descriptor_pool = descriptor_pool; self.descriptor_sets = descriptor_sets; self.command_buffers = command_buffers; 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_buffer(self.vertex_buffer, None) }; unsafe { device.free_memory(self.vertex_buffer_memory, None) }; unsafe { device.destroy_buffer(self.index_buffer, None) }; unsafe { device.free_memory(self.index_buffer_memory, None) }; unsafe { device.destroy_image(self.texture_image, None) }; unsafe { device.free_memory(self.texture_image_memory, None) }; unsafe { device.destroy_image_view(self.texture_image_view, 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) }; } // Notice that destroy_replaceable() freed the buffers in the pools, but // did not destroy the pools. unsafe { device.destroy_command_pool(self.primary_command_pool, None) }; unsafe { device.destroy_command_pool(self.transient_command_pool, None) }; } #[allow(unsafe_code)] fn destroy_replaceable(&mut self, device: &Device) { for framebuffer in &self.framebuffers { unsafe { device.destroy_framebuffer(*framebuffer, 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(self.primary_command_pool, &self.command_buffers) }; // 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_pipeline(self.pipeline, None) }; unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) }; unsafe { device.destroy_render_pass(self.render_pass, 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, physical_device: &vk::PhysicalDevice, device: &Device, indices: &QueueFamilyIndices) -> Result { let (capabilities, formats, presentation_modes) = PermanentGraphicsState::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, format.format)?; image_views.push(view); } Ok(Swapchain { swapchain, images, image_views, format: format.format, extent }) } #[allow(unsafe_code)] fn init_render_pass(device: &Device, format: &vk::Format) -> Result { let color_attachment = vk::AttachmentDescription::builder() .format(*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 subpass_attachments = [color_attachment_reference]; let subpass = vk::SubpassDescription::builder() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .color_attachments(&subpass_attachments); let dependency = vk::SubpassDependency::builder() .src_subpass(vk::SUBPASS_EXTERNAL) .src_stage_mask( vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .src_access_mask(vk::AccessFlags::empty()) .dst_subpass(0) .dst_stage_mask( vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE); let render_attachments = [color_attachment]; let subpasses = [subpass]; let dependencies = [dependency]; let render_pass_info = vk::RenderPassCreateInfo::builder() .attachments(&render_attachments) .subpasses(&subpasses) .dependencies(&dependencies); let render_pass = unsafe { device.create_render_pass(&render_pass_info, None) }?; Ok(render_pass) } #[allow(unsafe_code)] fn init_pipeline(device: &Device, descriptor_set_layout: &vk::DescriptorSetLayout, extent: &vk::Extent2D, render_pass: &vk::RenderPass) -> Result<(vk::PipelineLayout, vk::Pipeline)> { let vertex_binary = include_bytes!( concat!(env!("OUT_DIR"), "/shader.vert.spv")); let fragment_binary = include_bytes!( concat!(env!("OUT_DIR"), "/shader.frag.spv")); let vertex_module = PermanentGraphicsState::load_spirv_shader_module( device, vertex_binary)?; let fragment_module = PermanentGraphicsState::load_spirv_shader_module( device, fragment_binary)?; let vertex_stage_info = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::VERTEX) .module(vertex_module) .name(b"main\0"); let fragment_stage_info = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::FRAGMENT) .module(fragment_module) .name(b"main\0"); let binding_descriptions = [Vertex::::binding_description()]; let attribute_descriptions = Vertex::::attribute_descriptions(); let vertex_input_state_info = vk::PipelineVertexInputStateCreateInfo::builder() .vertex_binding_descriptions(&binding_descriptions) .vertex_attribute_descriptions(&attribute_descriptions); let input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo::builder() .topology(vk::PrimitiveTopology::TRIANGLE_LIST) .primitive_restart_enable(false); let viewport = vk::Viewport::builder() .x(0.0) .y(0.0) .width(extent.width as f32) .height(extent.height as f32) .min_depth(0.0) .max_depth(1.0); let viewports = [viewport]; let scissor = vk::Rect2D::builder() .offset(vk::Offset2D { x: 0, y: 0 }) .extent(*extent); let scissor_list = [scissor]; let viewport_state_info = vk::PipelineViewportStateCreateInfo::builder() .viewports(&viewports) .scissors(&scissor_list); let rasterizer_state_info = 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_info = vk::PipelineMultisampleStateCreateInfo::builder() .sample_shading_enable(false) .rasterization_samples(vk::SampleCountFlags::_1); let blend_attachment_info = 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 blend_attachments = [blend_attachment_info]; let blend_info = vk::PipelineColorBlendStateCreateInfo::builder() .logic_op_enable(false) .logic_op(vk::LogicOp::COPY) .attachments(&blend_attachments) .blend_constants([0.0, 0.0, 0.0, 0.0]); let layouts = [*descriptor_set_layout]; let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder() .set_layouts(&layouts); let pipeline_layout = unsafe { device.create_pipeline_layout(&pipeline_layout_info, None) }?; let stages = [vertex_stage_info, fragment_stage_info]; let pipeline_info = vk::GraphicsPipelineCreateInfo::builder() .stages(&stages) .vertex_input_state(&vertex_input_state_info) .input_assembly_state(&input_assembly_state_info) .viewport_state(&viewport_state_info) .rasterization_state(&rasterizer_state_info) .multisample_state(&multisample_state_info) .color_blend_state(&blend_info) .layout(pipeline_layout) .render_pass(*render_pass) .subpass(0); let pipeline = unsafe { device.create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None) }?.0[0]; unsafe { device.destroy_shader_module(vertex_module, None); device.destroy_shader_module(fragment_module, None); }; Ok((pipeline_layout, pipeline)) } #[allow(unsafe_code)] fn init_framebuffers(device: &Device, extent: &vk::Extent2D, swapchain_image_views: &Vec, render_pass: &vk::RenderPass) -> Result> { let mut framebuffers = Vec::new(); for image_view in swapchain_image_views { let attachments = [*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) }?; framebuffers.push(framebuffer); } Ok(framebuffers) } fn init_vertex_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool) -> Result<(vk::Buffer, vk::DeviceMemory)> { init_buffer(instance, physical_device, device, queue, command_pool, vk::BufferUsageFlags::VERTEX_BUFFER, &VERTICES) } fn init_index_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool) -> Result<(vk::Buffer, vk::DeviceMemory)> { init_buffer(instance, physical_device, device, queue, command_pool, vk::BufferUsageFlags::INDEX_BUFFER, INDICES) } #[allow(unsafe_code)] fn init_texture(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool) -> Result<(vk::Image, vk::DeviceMemory, vk::ImageView)> { let png = include_bytes!("../textures/forest_leaves_04_diff.png"); let decoder = Decoder::new(Cursor::new(png)); let mut reader = decoder.read_info()?; let (width, height) = reader.info().size(); let mut pixels = vec![0; reader.info().raw_bytes()]; reader.next_frame(&mut pixels)?; let (staging_buffer, staging_memory, _byte_size) = stage_in_buffer(instance, physical_device, device, &pixels)?; let (image, image_memory) = allocate_image(instance, physical_device, device, width, height, vk::Format::R8G8B8A8_SRGB, vk::ImageTiling::OPTIMAL, vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, vk::MemoryPropertyFlags::DEVICE_LOCAL)?; change_image_layout(device, queue, command_pool, &image, vk::Format::R8G8B8A8_SRGB, vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL)?; copy_buffer_to_image(device, queue, command_pool, &staging_buffer, &image, width, height)?; change_image_layout(device, queue, command_pool, &image, vk::Format::R8G8B8A8_SRGB, vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)?; let view = init_image_view(device, &image, vk::Format::R8G8B8A8_SRGB)?; unsafe { device.destroy_buffer(staging_buffer, None) }; unsafe { device.free_memory(staging_memory, None) }; Ok((image, image_memory, view)) } fn init_uniform_buffers(instance: &Instance, physical_device: &vk::PhysicalDevice, 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, physical_device, 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_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool, usage: vk::BufferUsageFlags, contents: &[T]) -> Result<(vk::Buffer, vk::DeviceMemory)> { let (staging_buffer, staging_memory, size) = stage_in_buffer(instance, physical_device, device, contents)?; let final_usage = vk::BufferUsageFlags::TRANSFER_DST | usage; let final_memory_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL; let (final_buffer, device_memory) = allocate_buffer(instance, physical_device, device, size as vk::DeviceSize, final_usage, final_memory_flags)?; copy_buffer(device, queue, command_pool, &staging_buffer, &final_buffer, size as vk::DeviceSize)?; unsafe { device.destroy_buffer(staging_buffer, None) }; unsafe { device.free_memory(staging_memory, None) }; Ok((final_buffer, device_memory)) } #[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::COMBINED_IMAGE_SAMPLER) .descriptor_count(count as u32); let sizes = [uniform_block_size, sampler_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_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout, buffers: &Vec, pool: &vk::DescriptorPool, count: usize, texture_image_view: &vk::ImageView, sampler: &vk::Sampler) -> Result> { let layouts = vec![*layout; count]; let set_info = vk::DescriptorSetAllocateInfo::builder() .descriptor_pool(*pool) .set_layouts(&layouts); let sets = unsafe { device.allocate_descriptor_sets(&set_info) }?; for index in 0 .. count { let buffer_info = vk::DescriptorBufferInfo::builder() .buffer(buffers[index]) .offset(0) .range(size_of::>() as vk::DeviceSize); let buffer_info_list = [buffer_info]; let uniform_block_write_info = vk::WriteDescriptorSet::builder() .dst_set(sets[index]) .dst_binding(0) .dst_array_element(0) .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) .buffer_info(&buffer_info_list); let image_info = vk::DescriptorImageInfo::builder() .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) .image_view(*texture_image_view) .sampler(*sampler); let image_info_list = [image_info]; let sampler_write_info = vk::WriteDescriptorSet::builder() .dst_set(sets[index]) .dst_binding(1) .dst_array_element(0) .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) .image_info(&image_info_list); let write_info_list = [uniform_block_write_info, sampler_write_info]; let copy_info_list: [vk::CopyDescriptorSet; 0] = []; unsafe { device.update_descriptor_sets(&write_info_list, ©_info_list) }; } Ok(sets) } #[allow(unsafe_code)] fn init_command_pools(device: &Device, indices: &QueueFamilyIndices) -> Result<(vk::CommandPool, vk::CommandPool)> { let command_pool_info = vk::CommandPoolCreateInfo::builder() .flags(vk::CommandPoolCreateFlags::empty()) .queue_family_index(indices.graphics); let primary = unsafe { device.create_command_pool(&command_pool_info, None) }?; command_pool_info.flags(vk::CommandPoolCreateFlags::TRANSIENT); let transient = unsafe { device.create_command_pool(&command_pool_info, None) }?; Ok((primary, transient)) } #[allow(unsafe_code)] fn init_commands(device: &Device, extent: &vk::Extent2D, framebuffers: &Vec, render_pass: &vk::RenderPass, pipeline_layout: &vk::PipelineLayout, pipeline: &vk::Pipeline, vertex_buffer: &vk::Buffer, index_buffer: &vk::Buffer, descriptor_sets: &Vec, command_pool: &vk::CommandPool) -> Result> { let command_buffer_allocation_info = vk::CommandBufferAllocateInfo::builder() .command_pool(*command_pool) .level(vk::CommandBufferLevel::PRIMARY) .command_buffer_count(framebuffers.len() as u32); let command_buffers = unsafe { device.allocate_command_buffers(&command_buffer_allocation_info) }?; for (index, framebuffer) in framebuffers.iter().enumerate() { let command_buffer = command_buffers[index]; let inheritance_info = vk::CommandBufferInheritanceInfo::builder(); let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() .flags(vk::CommandBufferUsageFlags::empty()) .inheritance_info(&inheritance_info); unsafe { device.begin_command_buffer(command_buffer, &command_buffer_begin_info) }?; let render_area = vk::Rect2D::builder() .offset(vk::Offset2D::default()) .extent(*extent); let clear_value = vk::ClearValue { color: vk::ClearColorValue { float32: [0.0, 0.0, 0.0, 1.0] } }; let clear_values = [clear_value]; let begin_pass_info = vk::RenderPassBeginInfo::builder() .render_pass(*render_pass) .framebuffer(*framebuffer) .render_area(render_area) .clear_values(&clear_values); unsafe { device.cmd_begin_render_pass(command_buffer, &begin_pass_info, vk::SubpassContents::INLINE) }; unsafe { device.cmd_bind_pipeline(command_buffer, vk::PipelineBindPoint::GRAPHICS, *pipeline) }; unsafe { device.cmd_bind_vertex_buffers(command_buffer, 0, &[*vertex_buffer], &[0]) }; unsafe { device.cmd_bind_index_buffer(command_buffer, *index_buffer, 0, vk::IndexType::UINT16) }; unsafe { device.cmd_bind_descriptor_sets(command_buffer, vk::PipelineBindPoint::GRAPHICS, *pipeline_layout, 0, &[descriptor_sets[index]], &[]) }; unsafe { device.cmd_draw_indexed(command_buffer, INDICES.len() as u32, 1, 0, 0, 0) }; unsafe { device.cmd_end_render_pass(command_buffer) }; unsafe { device.end_command_buffer(command_buffer) }?; } Ok(command_buffers) } #[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, }) } #[allow(unsafe_code)] fn init_image_view(device: &Device, image: &vk::Image, format: vk::Format) -> Result { 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 view_info = vk::ImageViewCreateInfo::builder() .image(*image) .view_type(vk::ImageViewType::_2D) .format(format) .components(components) .subresource_range(subresource_range); let view = unsafe { device.create_image_view(&view_info, None) }?; Ok(view) } 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()); } 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()) } } #[allow(unsafe_code)] fn pick_memory_type(instance: &Instance, physical_device: &vk::PhysicalDevice, properties: &vk::MemoryPropertyFlags, requirements: &vk::MemoryRequirements) -> Result { let memory_map = unsafe { instance.get_physical_device_memory_properties(*physical_device) }; // So. The memory_type_bits field is a map of which indices are suitable, // based on the buffer our caller passed to // get_buffer_memory_requirements(). Yes, that means there's a hard cap on // how many memory types there can be, based on the size of the bitfield. for index in 0 .. memory_map.memory_type_count { if requirements.memory_type_bits & (1 << index) == 0 { continue; } let memory_type = memory_map.memory_types[index as usize]; if memory_type.property_flags.contains(*properties) { return Ok(index); } } Err(Error { message: "The system has no suitable memory for a buffer.".to_string() }) } #[allow(unsafe_code)] fn stage_in_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, contents: &[T]) -> Result<(vk::Buffer, vk::DeviceMemory, usize)> { let size = size_of::() * contents.len(); let staging_usage = vk::BufferUsageFlags::TRANSFER_SRC; let staging_memory_flags = vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE; let (staging_buffer, staging_memory) = allocate_buffer(instance, physical_device, device, size as vk::DeviceSize, staging_usage, staging_memory_flags)?; let host_memory = unsafe { device.map_memory(staging_memory, 0, size as vk::DeviceSize, vk::MemoryMapFlags::empty()) }?; unsafe { copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len()) }; unsafe { device.unmap_memory(staging_memory) }; Ok((staging_buffer, staging_memory, size)) } #[allow(unsafe_code)] fn allocate_buffer(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, size: vk::DeviceSize, usage: vk::BufferUsageFlags, memory_flags: vk::MemoryPropertyFlags) -> Result<(vk::Buffer, vk::DeviceMemory)> { let buffer_info = vk::BufferCreateInfo::builder() .size(size) .usage(usage) .sharing_mode(vk::SharingMode::EXCLUSIVE); let buffer = unsafe { device.create_buffer(&buffer_info, None) }?; // The requirements are mostly what you'd think: size and alignment. The // bits field is something special; see pick_memory_type() for the // explanation. Despite the simplicity of this data, Vulkan wants to be the // one to tell us about it, and we let it. let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; let type_index = pick_memory_type(instance, physical_device, &memory_flags, &requirements)?; let memory_info = vk::MemoryAllocateInfo::builder() .allocation_size(requirements.size) .memory_type_index(type_index); let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?; unsafe { device.bind_buffer_memory(buffer, device_memory, 0) }?; Ok((buffer, device_memory)) } #[allow(unsafe_code)] fn copy_buffer(device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool, source: &vk::Buffer, destination: &vk::Buffer, size: vk::DeviceSize) -> Result<()> { let command_buffer = begin_transient_commands(device, command_pool)?; let copy_info = vk::BufferCopy::builder().size(size); unsafe { device.cmd_copy_buffer(command_buffer, *source, *destination, &[copy_info]) }; end_transient_commands(command_buffer, device, queue, command_pool)?; Ok(()) } #[allow(unsafe_code)] fn allocate_image(instance: &Instance, physical_device: &vk::PhysicalDevice, device: &Device, width: u32, height: u32, format: vk::Format, tiling: vk::ImageTiling, usage: vk::ImageUsageFlags, memory_flags: vk::MemoryPropertyFlags) -> Result<(vk::Image, vk::DeviceMemory)> { let image_info = vk::ImageCreateInfo::builder() .image_type(vk::ImageType::_2D) .extent(vk::Extent3D { width, height, depth: 1 }) .mip_levels(1) .array_layers(1) .format(format) .tiling(tiling) .initial_layout(vk::ImageLayout::UNDEFINED) .usage(usage) .sharing_mode(vk::SharingMode::EXCLUSIVE) .samples(vk::SampleCountFlags::_1) .flags(vk::ImageCreateFlags::empty()); let image = unsafe { device.create_image(&image_info, None) }?; let requirements = unsafe { device.get_image_memory_requirements(image) }; let type_index = pick_memory_type(instance, physical_device, &memory_flags, &requirements)?; let image_memory_info = vk::MemoryAllocateInfo::builder() .allocation_size(requirements.size) .memory_type_index(type_index); let image_memory = unsafe { device.allocate_memory(&image_memory_info, None) }?; unsafe { device.bind_image_memory(image, image_memory, 0) }?; Ok((image, image_memory)) } #[allow(unsafe_code)] fn copy_buffer_to_image(device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool, source: &vk::Buffer, destination: &vk::Image, width: u32, height: u32) -> Result<()> { let command_buffer = begin_transient_commands(device, command_pool)?; let subresource_layers = vk::ImageSubresourceLayers::builder() .aspect_mask(vk::ImageAspectFlags::COLOR) .mip_level(0) .base_array_layer(0) .layer_count(1); let copy_info = vk::BufferImageCopy::builder() .buffer_offset(0) .buffer_row_length(0) .buffer_image_height(0) .image_subresource(subresource_layers) .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 }) .image_extent(vk::Extent3D { width, height, depth: 1 }); unsafe { device.cmd_copy_buffer_to_image(command_buffer, *source, *destination, vk::ImageLayout::TRANSFER_DST_OPTIMAL, &[copy_info]) }; end_transient_commands(command_buffer, device, queue, command_pool)?; Ok(()) } #[allow(unsafe_code)] fn change_image_layout(device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool, image: &vk::Image, format: vk::Format, old: vk::ImageLayout, new: vk::ImageLayout) -> Result<()> { let command_buffer = begin_transient_commands(device, command_pool)?; 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); // Notionally this is a property that our caller is in a better position // to know than we are, but in practice the nature of the transition // strongly implies a particular phase of the image's lifecycle, so we just // compute it here. let (source_access, source_stage, destination_access, destination_stage) = match (old, new) { (vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL) => (vk::AccessFlags::empty(), vk::PipelineStageFlags::TOP_OF_PIPE, vk::AccessFlags::TRANSFER_WRITE, vk::PipelineStageFlags::TRANSFER), (vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) => (vk::AccessFlags::TRANSFER_WRITE, vk::PipelineStageFlags::TRANSFER, vk::AccessFlags::SHADER_READ, vk::PipelineStageFlags::FRAGMENT_SHADER), _ => return Err(Error { message: format!("Don't know how to change from image layout {:?} to {:?}", old, new) }) }; let barrier_info = vk::ImageMemoryBarrier::builder() .image(*image) .subresource_range(subresource_range) .old_layout(old) .new_layout(new) .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) .src_access_mask(source_access) .dst_access_mask(destination_access); unsafe { device.cmd_pipeline_barrier(command_buffer, source_stage, destination_stage, vk::DependencyFlags::empty(), &[] as &[vk::MemoryBarrier], &[] as &[vk::BufferMemoryBarrier], &[barrier_info]) }; end_transient_commands(command_buffer, device, queue, command_pool)?; Ok(()) } #[allow(unsafe_code)] fn begin_transient_commands(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(1); let command_buffer = unsafe { device.allocate_command_buffers(&command_buffer_allocation_info) }?[0]; let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); unsafe { device.begin_command_buffer(command_buffer, &command_buffer_begin_info) }?; Ok(command_buffer) } #[allow(unsafe_code)] fn end_transient_commands(command_buffer: vk::CommandBuffer, device: &Device, queue: &vk::Queue, command_pool: &vk::CommandPool) -> Result<()> { unsafe { device.end_command_buffer(command_buffer) }?; let command_buffers = [command_buffer]; let submit_info = vk::SubmitInfo::builder() .command_buffers(&command_buffers); unsafe { device.queue_submit(*queue, &[submit_info], vk::Fence::null()) }?; unsafe { device.queue_wait_idle(*queue) }?; unsafe { device.free_command_buffers(*command_pool, &command_buffers) }; Ok(()) }