diff options
Diffstat (limited to 'src/graphics/window_dressing.rs')
| -rw-r--r-- | src/graphics/window_dressing.rs | 458 |
1 files changed, 2 insertions, 456 deletions
diff --git a/src/graphics/window_dressing.rs b/src/graphics/window_dressing.rs index 09410bc..9afb03e 100644 --- a/src/graphics/window_dressing.rs +++ b/src/graphics/window_dressing.rs @@ -5,17 +5,13 @@ use crate::graphics::permanent::{ EnableAnisotropy }; use crate::graphics::util::{ - allocate_buffer, stage_in_buffer, - pick_memory_type, - begin_transient_commands, end_transient_commands + allocate_buffer, allocate_image, init_image_view }; use crate::shader_data::UniformBlock; use std::collections::BTreeSet; -use std::io::Cursor; use std::mem::size_of; -use png::Decoder; use vulkanalia::{ Device, Instance }; use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0, KhrSwapchainExtensionDeviceCommands }; @@ -46,12 +42,6 @@ pub struct WindowDressing { pub depth_image_view: vk::ImageView, pub depth_format: vk::Format, - pub primary_command_pool: vk::CommandPool, - pub transient_command_pool: vk::CommandPool, - - texture_image: vk::Image, - texture_image_memory: vk::DeviceMemory, - pub texture_image_view: vk::ImageView, pub sampler: vk::Sampler, pub uniform_buffers: Vec<vk::Buffer>, @@ -101,14 +91,13 @@ pub struct Concurrency { impl WindowDressing { pub fn new(permanent: &PermanentGraphicsState, for_reinit: &GraphicsStateForReinit, - enable_anisotropy: EnableAnisotropy) + enable_anisotropy: EnableAnisotropy, mip_count: u32) -> Result<Self> { let window = &permanent.window; let instance = &permanent.instance; let surface = &permanent.surface; let device = &permanent.device; - let graphics_queue = &permanent.graphics_queue; let sample_count = for_reinit.sample_count; let indices = &for_reinit.indices; @@ -122,13 +111,6 @@ impl WindowDressing { let (depth_image, depth_image_memory, depth_image_view, depth_format) = init_depth(instance, device, &swapchain.extent, sample_count)?; - let (primary_command_pool, transient_command_pool) - = init_command_pools(device, indices)?; - - let (texture_image, texture_image_memory, texture_image_view, mip_count) - = init_texture(instance, device, graphics_queue, - &transient_command_pool)?; - let sampler = init_sampler(&device, &enable_anisotropy, mip_count)?; let (uniform_buffers, uniform_buffer_memory) @@ -148,15 +130,10 @@ impl WindowDressing { depth_image_memory, depth_image_view, depth_format, - texture_image, - texture_image_memory, - texture_image_view, sampler, uniform_buffers, uniform_buffer_memory, descriptor_pool, - primary_command_pool, - transient_command_pool, concurrency, }) } @@ -220,9 +197,6 @@ impl WindowDressing { pub fn destroy(mut self, device: &Device) { self.destroy_replaceable(device); - 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) }; unsafe { device.destroy_sampler(self.sampler, None) }; for semaphore in self.concurrency.image_available_semaphores { @@ -236,11 +210,6 @@ impl WindowDressing { 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) }; } @@ -400,74 +369,6 @@ fn init_depth(instance: &Instance, device: &Device, extent: &vk::Extent2D, } -#[allow(unsafe_code)] -fn init_texture(instance: &Instance, device: &Device, queue: &vk::Queue, - command_pool: &vk::CommandPool) - -> Result<(vk::Image, vk::DeviceMemory, vk::ImageView, u32)> -{ - let physical_device = device.physical_device(); - - 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 format_properties = unsafe { - instance.get_physical_device_format_properties(physical_device, - vk::Format::R8G8B8A8_SRGB) - }; - let has_linear_filter = format_properties - .optimal_tiling_features - .contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR); - let mip_count = if has_linear_filter { - // This will generate mips all the way down to 1x1. It is not clear - // whether there's a benefit to that. - (width.max(height)).ilog2() + 1 - } else { - 1 - }; - - 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, device, &pixels)?; - - let (image, image_memory) - = allocate_image(instance, device, - width, height, mip_count, vk::SampleCountFlags::_1, - vk::Format::R8G8B8A8_SRGB, - vk::ImageTiling::OPTIMAL, - vk::ImageUsageFlags::SAMPLED - | vk::ImageUsageFlags::TRANSFER_SRC - | vk::ImageUsageFlags::TRANSFER_DST, - vk::MemoryPropertyFlags::DEVICE_LOCAL)?; - - change_image_layout(device, queue, command_pool, &image, mip_count, - vk::ImageLayout::UNDEFINED, - vk::ImageLayout::TRANSFER_DST_OPTIMAL)?; - - copy_buffer_to_image(device, queue, command_pool, &staging_buffer, &image, - width, height)?; - - // This will also change the layout to SHADER_READ_ONLY_OPTIMAL. - - fill_mip_levels(device, queue, command_pool, &image, - width, height, mip_count)?; - - let view = init_image_view(device, &image, mip_count, - vk::Format::R8G8B8A8_SRGB, - vk::ImageAspectFlags::COLOR)?; - - unsafe { device.destroy_buffer(staging_buffer, None) }; - unsafe { device.free_memory(staging_memory, None) }; - - Ok((image, image_memory, view, mip_count)) -} - - fn init_uniform_buffers(instance: &Instance, device: &Device, count: usize) -> Result<(Vec<vk::Buffer>, Vec<vk::DeviceMemory>)> { @@ -545,28 +446,6 @@ fn init_descriptor_pool(device: &Device, count: usize) #[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::TRANSIENT - | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) - .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_concurrency(device: &Device, swapchain_images: &Vec<vk::Image>) -> Result<Concurrency> @@ -606,41 +485,6 @@ fn init_concurrency(device: &Device, } -#[allow(unsafe_code)] -fn init_image_view(device: &Device, image: &vk::Image, mip_count: u32, - format: vk::Format, aspects: vk::ImageAspectFlags) - -> Result<vk::ImageView> -{ - // Component mapping is only for color components (not, for example, depth - // or stencil components), so we always just want it like this. - 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(aspects) - .base_mip_level(0) - .level_count(mip_count) - .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<vk::SurfaceFormatKHR>) -> Result<vk::SurfaceFormatKHR> { @@ -713,301 +557,3 @@ fn pick_image_extent(window: &Window, } } - -#[allow(unsafe_code)] -fn allocate_image(instance: &Instance, device: &Device, width: u32, - height: u32, mip_count: u32, - sample_count: vk::SampleCountFlags, format: vk::Format, - tiling: vk::ImageTiling, usage: vk::ImageUsageFlags, - memory_flags: vk::MemoryPropertyFlags) - -> Result<(vk::Image, vk::DeviceMemory)> -{ - let physical_device = device.physical_device(); - - let image_info = vk::ImageCreateInfo::builder() - .image_type(vk::ImageType::_2D) - .extent(vk::Extent3D { width, height, depth: 1 }) - .mip_levels(mip_count) - .samples(sample_count) - .array_layers(1) - .format(format) - .tiling(tiling) - .initial_layout(vk::ImageLayout::UNDEFINED) - .usage(usage) - .sharing_mode(vk::SharingMode::EXCLUSIVE) - .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, - mip_count: u32, 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(mip_count) - .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(()) -} - - -// An Image can store multiple mip levels within it, as one of several kinds -// of subresource it has. We deal with this by -#[allow(unsafe_code)] -fn fill_mip_levels(device: &Device, queue: &vk::Queue, - command_pool: &vk::CommandPool, image: &vk::Image, - original_width: u32, original_height: u32, - mip_count: u32) - -> Result<()> -{ - let command_buffer = begin_transient_commands(device, command_pool)?; - - // We'll be mutating these two builders as we loop through the mip levels, - // because we need to construct a lot of similar things. Remember, the - // builder methods don't mutate in-place, they return a new builder; to - // avoid confusion we always assign that result back to the same variable. - let mut barrier_subresource_range = vk::ImageSubresourceRange::builder() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .level_count(1) - .base_array_layer(0) - .layer_count(1); - - let mut blit_barrier_info = vk::ImageMemoryBarrier::builder() - .image(*image) - .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) - .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED); - - // Now we loop through the mip levels from largest (low numbers) to - // smallest (high numbers). Conceptually, the only thing we're doing is a - // blit that copies each mip level from the one immediately before. Recall - // though that we don't just want to fill in the pixels, we also care about - // pixel format and memory sharing. There are additional operations to deal - // with that. These are best done together, as detailed below. - // - // This loop has a lot of code in it, so we make the "paragraphs" a little - // more dense than usual to make sure the logical grouping is clear. - let mut source_width = original_width; - let mut source_height = original_height; - for destination_mip_level in 1 .. mip_count { - let source_mip_level = destination_mip_level - 1; - let destination_width = (source_width / 2).max(1); - let destination_height = (source_height / 2).max(1); - - // So. The name pipeline_barrier is a little misleading; it does indeed - // mean "barrier" in the concurrency sense, but it isn't just initiating - // a wait, it's also performing any needed mutation. We do one of them - // here, acting on this iteration's source level, to set it up for - // reading. - barrier_subresource_range = barrier_subresource_range - .base_mip_level(source_mip_level as u32); - blit_barrier_info = blit_barrier_info - .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) - .new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL) - .src_access_mask(vk::AccessFlags::TRANSFER_WRITE) - .dst_access_mask(vk::AccessFlags::TRANSFER_READ) - .subresource_range(barrier_subresource_range); - unsafe { - device.cmd_pipeline_barrier(command_buffer, - vk::PipelineStageFlags::TRANSFER, - vk::PipelineStageFlags::TRANSFER, - vk::DependencyFlags::empty(), - &[] as &[vk::MemoryBarrier], - &[] as &[vk::BufferMemoryBarrier], - &[blit_barrier_info]) - }; - - // Now we do the actual blit. Nice and easy, though specifying the - // coordinates is a bit verbose. - let blit_source_layer_info = vk::ImageSubresourceLayers::builder() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .mip_level(source_mip_level as u32) - .base_array_layer(0) - .layer_count(1); - let blit_destination_layer_info = vk::ImageSubresourceLayers::builder() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .mip_level(destination_mip_level as u32) - .base_array_layer(0) - .layer_count(1); - let blit_info = vk::ImageBlit::builder() - .src_offsets([vk::Offset3D { x: 0, y: 0, z: 0 }, - vk::Offset3D { - x: source_width as i32, - y: source_height as i32, - z: 1 - }]) - .src_subresource(blit_source_layer_info) - .dst_offsets([vk::Offset3D { x: 0, y: 0, z: 0 }, - vk::Offset3D { - x: destination_width as i32, - y: destination_height as i32, - z: 1 - }]) - .dst_subresource(blit_destination_layer_info); - unsafe { - device.cmd_blit_image(command_buffer, - *image, vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - *image, vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &[blit_info], - vk::Filter::LINEAR) - }; - - // Now we do another pipeline_barrier. We're still acting on this - // iteration's source level, not on the destination. We'll never need to - // use it again except from the shader, so we set it appropriately for - // that. - blit_barrier_info = blit_barrier_info - .old_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL) - .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .src_access_mask(vk::AccessFlags::TRANSFER_READ) - .dst_access_mask(vk::AccessFlags::SHADER_READ); - unsafe { - device.cmd_pipeline_barrier(command_buffer, - vk::PipelineStageFlags::TRANSFER, - vk::PipelineStageFlags::FRAGMENT_SHADER, - vk::DependencyFlags::empty(), - &[] as &[vk::MemoryBarrier], - &[] as &[vk::BufferMemoryBarrier], - &[blit_barrier_info]) - }; - - source_width = destination_width; - source_height = destination_height; - } - - let final_mip_level = mip_count - 1; - - // We need to do one final pipeline_barrier, because the loop didn't do it - // to the smallest (last) mip level. We change it to have the same settings - // the loop left the rest of them in. The barrier source properties for this - // barrier are different from the others because this level was never useds - // as a blit source, only as a blit destination. The barrier destination - // properties are the same as the rest, so after this all the subresourcess - // will be in their fully-ready state. - barrier_subresource_range = barrier_subresource_range - .base_mip_level(final_mip_level as u32); - blit_barrier_info = blit_barrier_info - .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) - .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .src_access_mask(vk::AccessFlags::TRANSFER_WRITE) - .dst_access_mask(vk::AccessFlags::SHADER_READ) - .subresource_range(barrier_subresource_range); - unsafe { - device.cmd_pipeline_barrier(command_buffer, - vk::PipelineStageFlags::TRANSFER, - vk::PipelineStageFlags::FRAGMENT_SHADER, - vk::DependencyFlags::empty(), - &[] as &[vk::MemoryBarrier], - &[] as &[vk::BufferMemoryBarrier], - &[blit_barrier_info]) - }; - - end_transient_commands(command_buffer, device, queue, command_pool)?; - - Ok(()) -} - |