diff --git a/src/filters.rs b/src/filters.rs index e4bbaab..3637cb5 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -6,11 +6,46 @@ // --------------------------------------------------------------------- / imports -use crate::vulkan::{VulkanCore, VulkanTexture, create_texture}; +use crate::vulkan::{VulkanCore, VulkanTexture}; use std::error::Error; use ash::vk; +// --------------------------------------------------------------------- / linear sampler + +pub fn linear_sampler(device: &ash::Device) -> Result> { + let info = vk::SamplerCreateInfo::default() + .mag_filter(vk::Filter::LINEAR) + .min_filter(vk::Filter::LINEAR) + .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE) + .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE) + .address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE); + unsafe { Ok(device.create_sampler(&info, None)?) } +} + + +// --------------------------------------------------------------------- / 2D image view + +pub fn image_view( + device: &ash::Device, + image: vk::Image, + format: vk::Format, +) -> Result> { + let view_info = vk::ImageViewCreateInfo::default() + .image(image) + .view_type(vk::ImageViewType::TYPE_2D) + .format(format) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }); + unsafe { Ok(device.create_image_view(&view_info, None)?) } +} + + // --------------------------------------------------------------------- / generic compute pub fn compute_pipeline( @@ -19,20 +54,20 @@ pub fn compute_pipeline( bindings: &[vk::DescriptorSetLayoutBinding], ) -> Result<(vk::ShaderModule, vk::Pipeline, vk::DescriptorSetLayout), Box> { - // Shader module + // shader module let create_info = vk::ShaderModuleCreateInfo::default().code(spv); let module = unsafe { device.create_shader_module(&create_info, None)? }; - // Descriptor set layout + // descriptor set layout let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(bindings); let desc_layout = unsafe { device.create_descriptor_set_layout(&layout_info, None)? }; - // Pipeline layout + // pipeline layout let set_layouts = [desc_layout]; let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts); let pipeline_layout = unsafe { device.create_pipeline_layout(&pipeline_layout_info, None)? }; - // Compute pipeline + // compute pipeline let stage = vk::PipelineShaderStageCreateInfo::default() .stage(vk::ShaderStageFlags::COMPUTE) .module(module) @@ -83,222 +118,165 @@ pub fn filter_pipeline( } -// --------------------------------------------------------------------- / blur texture +// --------------------------------------------------------------------- / compute filter -pub fn blur_texture( +pub unsafe fn compute_filter( vk_core: &VulkanCore, - input_texture: &VulkanTexture, + _input_image: vk::Image, + output_image: vk::Image, width: u32, height: u32, - blur_pipeline: vk::Pipeline, - blur_desc_layout: vk::DescriptorSetLayout, -) -> Result> { - - // create the output texture (same size, with STORAGE + TRANSFER_SRC) - let (output_image, output_memory) = create_texture( - &vk_core.instance, - &vk_core.device, - vk_core.physical_device, - width, - height, - vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::STORAGE, - vk::Format::R8G8B8A8_UNORM, - )?; - - // create image views and a sampler - let input_view = { - let view_info = vk::ImageViewCreateInfo::default() - .image(input_texture.image) - .view_type(vk::ImageViewType::TYPE_2D) - .format(vk::Format::R8G8B8A8_SRGB) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - unsafe { vk_core.device.create_image_view(&view_info, None)? } - }; - let output_view = { - let view_info = vk::ImageViewCreateInfo::default() - .image(output_image) - .view_type(vk::ImageViewType::TYPE_2D) - .format(vk::Format::R8G8B8A8_UNORM) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - unsafe { vk_core.device.create_image_view(&view_info, None)? } - }; - let sampler_info = vk::SamplerCreateInfo::default() - .mag_filter(vk::Filter::LINEAR) - .min_filter(vk::Filter::LINEAR) - .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE) - .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE) - .address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE); - let sampler = unsafe { vk_core.device.create_sampler(&sampler_info, None)? }; - - // descriptor set (bindings 0=sampler, 1=storage image) + pipeline: vk::Pipeline, + descriptor_set_layout: vk::DescriptorSetLayout, + configure: impl FnOnce(vk::DescriptorSet), +) -> Result<(), Box> { let pool_sizes = [ - vk::DescriptorPoolSize { - ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, - descriptor_count: 1, - }, - vk::DescriptorPoolSize { - ty: vk::DescriptorType::STORAGE_IMAGE, - descriptor_count: 1, - }, + vk::DescriptorPoolSize { ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, descriptor_count: 1 }, + vk::DescriptorPoolSize { ty: vk::DescriptorType::STORAGE_IMAGE, descriptor_count: 1 }, ]; - let pool_info = vk::DescriptorPoolCreateInfo::default() - .max_sets(1) - .pool_sizes(&pool_sizes); + let pool_info = vk::DescriptorPoolCreateInfo::default().max_sets(1).pool_sizes(&pool_sizes); let desc_pool = unsafe { vk_core.device.create_descriptor_pool(&pool_info, None)? }; - let set_layouts = [blur_desc_layout]; + let set_layouts = [descriptor_set_layout]; let alloc_info = vk::DescriptorSetAllocateInfo::default() .descriptor_pool(desc_pool) .set_layouts(&set_layouts); let desc_sets = unsafe { vk_core.device.allocate_descriptor_sets(&alloc_info)? }; let desc_set = desc_sets[0]; - let input_image_info = vk::DescriptorImageInfo::default() - .sampler(sampler) - .image_view(input_view) - .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL); - let output_image_info = vk::DescriptorImageInfo::default() - .image_view(output_view) - .image_layout(vk::ImageLayout::GENERAL); - let input_image_infos = [input_image_info]; - let output_image_infos = [output_image_info]; - let write_descriptors = [ - vk::WriteDescriptorSet::default() - .dst_set(desc_set) - .dst_binding(0) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .image_info(&input_image_infos), - vk::WriteDescriptorSet::default() - .dst_set(desc_set) - .dst_binding(1) - .descriptor_type(vk::DescriptorType::STORAGE_IMAGE) - .image_info(&output_image_infos), - ]; - unsafe { vk_core.device.update_descriptor_sets(&write_descriptors, &[]) }; + configure(desc_set); - // pipeline layout (no push constants, same descriptor layout as the pipeline) - let set_layouts2 = [blur_desc_layout]; - let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default() - .set_layouts(&set_layouts2); + let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts); let pipeline_layout = unsafe { vk_core.device.create_pipeline_layout(&pipeline_layout_info, None)? }; - // record compute commands - unsafe { - vk_core.device.reset_command_pool(vk_core.command_pool, vk::CommandPoolResetFlags::empty())?; - } - let begin_info = vk::CommandBufferBeginInfo::default() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); - unsafe { vk_core.device.begin_command_buffer(vk_core.command_buffer, &begin_info) }?; - - // transition output to GENERAL - let barrier = vk::ImageMemoryBarrier::default() - .image(output_image) - .old_layout(vk::ImageLayout::UNDEFINED) - .new_layout(vk::ImageLayout::GENERAL) - .src_access_mask(vk::AccessFlags::empty()) - .dst_access_mask(vk::AccessFlags::SHADER_WRITE) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - unsafe { - vk_core.device.cmd_pipeline_barrier( - vk_core.command_buffer, - vk::PipelineStageFlags::TOP_OF_PIPE, - vk::PipelineStageFlags::COMPUTE_SHADER, - vk::DependencyFlags::empty(), - &[], - &[], - &[barrier], - ); - } + vk_core.record_commands(|command_buffer| { + let barrier = vk::ImageMemoryBarrier::default() + .image(output_image) + .old_layout(vk::ImageLayout::UNDEFINED) + .new_layout(vk::ImageLayout::GENERAL) + .src_access_mask(vk::AccessFlags::empty()) + .dst_access_mask(vk::AccessFlags::SHADER_WRITE) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, level_count: 1, + base_array_layer: 0, layer_count: 1, + }); + unsafe { + vk_core.device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::COMPUTE_SHADER, + vk::DependencyFlags::empty(), + &[], &[], &[barrier], + ); + } + + unsafe { + vk_core.device.cmd_bind_pipeline(command_buffer, vk::PipelineBindPoint::COMPUTE, pipeline); + vk_core.device.cmd_bind_descriptor_sets( + command_buffer, + vk::PipelineBindPoint::COMPUTE, + pipeline_layout, + 0, &[desc_set], &[], + ); + } + + let group_x = (width + 15) / 16; + let group_y = (height + 15) / 16; + unsafe { vk_core.device.cmd_dispatch(command_buffer, group_x, group_y, 1) }; + + let barrier2 = vk::ImageMemoryBarrier::default() + .image(output_image) + .old_layout(vk::ImageLayout::GENERAL) + .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) + .src_access_mask(vk::AccessFlags::SHADER_WRITE) + .dst_access_mask(vk::AccessFlags::SHADER_READ) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, level_count: 1, + base_array_layer: 0, layer_count: 1, + }); + unsafe { + vk_core.device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::COMPUTE_SHADER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::DependencyFlags::empty(), + &[], &[], &[barrier2], + ); + } + })?; - // bind pipeline and descriptor set unsafe { - vk_core.device.cmd_bind_pipeline( - vk_core.command_buffer, - vk::PipelineBindPoint::COMPUTE, - blur_pipeline, - ); - vk_core.device.cmd_bind_descriptor_sets( - vk_core.command_buffer, - vk::PipelineBindPoint::COMPUTE, - pipeline_layout, - 0, - &[desc_set], - &[], - ); + vk_core.device.destroy_descriptor_pool(desc_pool, None); + vk_core.device.destroy_pipeline_layout(pipeline_layout, None); } - // dispatch - let group_x = (width + 15) / 16; - let group_y = (height + 15) / 16; - unsafe { vk_core.device.cmd_dispatch(vk_core.command_buffer, group_x, group_y, 1) }; - - // transition output to SHADER_READ_ONLY_OPTIMAL for later blitting - let barrier2 = vk::ImageMemoryBarrier::default() - .image(output_image) - .old_layout(vk::ImageLayout::GENERAL) - .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .src_access_mask(vk::AccessFlags::SHADER_WRITE) - .dst_access_mask(vk::AccessFlags::SHADER_READ) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); + Ok(()) +} + + +// --------------------------------------------------------------------- / blur texture + +pub fn blur_texture( + vk_core: &VulkanCore, + input_texture: &VulkanTexture, + width: u32, + height: u32, + blur_pipeline: vk::Pipeline, + blur_desc_layout: vk::DescriptorSetLayout, +) -> Result> { + let (output_image, output_memory) = vk_core.create_texture( + width, height, + vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::STORAGE, + vk::Format::R8G8B8A8_UNORM, + )?; + + let input_view = image_view(&vk_core.device, input_texture.image, vk::Format::R8G8B8A8_SRGB)?; + let output_view = image_view(&vk_core.device, output_image, vk::Format::R8G8B8A8_UNORM)?; + let sampler = linear_sampler(&vk_core.device)?; + unsafe { - vk_core.device.cmd_pipeline_barrier( - vk_core.command_buffer, - vk::PipelineStageFlags::COMPUTE_SHADER, - vk::PipelineStageFlags::FRAGMENT_SHADER, - vk::DependencyFlags::empty(), - &[], - &[], - &[barrier2], - ); + compute_filter( + vk_core, + input_texture.image, + output_image, + width, height, + blur_pipeline, + blur_desc_layout, + |desc_set| { + let input_info = vk::DescriptorImageInfo::default() + .sampler(sampler) + .image_view(input_view) + .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL); + let output_info = vk::DescriptorImageInfo::default() + .image_view(output_view) + .image_layout(vk::ImageLayout::GENERAL); + let input_infos = [input_info]; + let output_infos = [output_info]; + let writes = [ + vk::WriteDescriptorSet::default() + .dst_set(desc_set) + .dst_binding(0) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(&input_infos), + vk::WriteDescriptorSet::default() + .dst_set(desc_set) + .dst_binding(1) + .descriptor_type(vk::DescriptorType::STORAGE_IMAGE) + .image_info(&output_infos), + ]; + vk_core.device.update_descriptor_sets(&writes, &[]); + }, + )?; } - unsafe { vk_core.device.end_command_buffer(vk_core.command_buffer) }?; - - // submit - let command_buffers = [vk_core.command_buffer]; - let submit_info = vk::SubmitInfo::default().command_buffers(&command_buffers); - let fence = unsafe { vk_core.device.create_fence(&vk::FenceCreateInfo::default(), None) }?; - unsafe { vk_core.device.queue_submit(vk_core.graphics_queue, &[submit_info], fence) }?; - unsafe { vk_core.device.wait_for_fences(&[fence], true, u64::MAX) }?; - unsafe { vk_core.device.destroy_fence(fence, None) }; - // cleanup transient objects unsafe { - vk_core.device.destroy_sampler(sampler, None); vk_core.device.destroy_image_view(input_view, None); vk_core.device.destroy_image_view(output_view, None); - vk_core.device.destroy_descriptor_pool(desc_pool, None); - vk_core.device.destroy_pipeline_layout(pipeline_layout, None); + vk_core.device.destroy_sampler(sampler, None); } - Ok(VulkanTexture { - image: output_image, - _memory: output_memory, - width, - height, - }) + Ok(VulkanTexture { image: output_image, _memory: output_memory, width, height }) } diff --git a/src/vulkan.rs b/src/vulkan.rs index 37ea84e..944d219 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -239,565 +239,523 @@ pub fn vulkan_surfchain( } -// --------------------------------------------------------------------- / vulkan wrapper +// --------------------------------------------------------------------- / record commands -pub fn vulkan_pipeline( - vk_core: &VulkanCore, - pixel_bytes: &[u8], - width: u32, - height: u32, -) -> Result> { - - // copy raw image data to staging buffer - let (buffer, memory) = create_buffer( - &vk_core.instance, - &vk_core.device, - vk_core.physical_device, - pixel_bytes, - )?; - - // allocate vram for image data - let (texture, vram) = create_texture( - &vk_core.instance, - &vk_core.device, - vk_core.physical_device, - width, height, - vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED, - vk::Format::R8G8B8A8_SRGB, - )?; - - // load pixel data from buffer to texture - load_texture( - &vk_core.device, - vk_core.graphics_queue, - vk_core.command_pool, - vk_core.command_buffer, - buffer, - texture, - width, - height, - )?; - - // drop the staging buffer (no longer needed) - unsafe { - vk_core.device.destroy_buffer(buffer, None); - vk_core.device.free_memory(memory, None); +impl VulkanCore { + pub(crate) fn record_commands( + &self, + f: impl FnOnce(vk::CommandBuffer), + ) -> Result<(), Box> { + unsafe { + self.device + .reset_command_pool(self.command_pool, vk::CommandPoolResetFlags::empty())?; + } + + let begin_info = vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); + unsafe { + self.device + .begin_command_buffer(self.command_buffer, &begin_info)?; + } + + f(self.command_buffer); + unsafe { + self.device.end_command_buffer(self.command_buffer)?; + } + + let submit_info = vk::SubmitInfo::default() + .command_buffers(std::slice::from_ref(&self.command_buffer)); + let fence = unsafe { + self.device + .create_fence(&vk::FenceCreateInfo::default(), None)? + }; + unsafe { + self.device + .queue_submit(self.graphics_queue, &[submit_info], fence)?; + self.device + .wait_for_fences(&[fence], true, u64::MAX)?; + self.device.destroy_fence(fence, None); + } + + Ok(()) } +} - Ok(VulkanTexture { - image: texture, - _memory: vram, - width, - height, - }) + +// --------------------------------------------------------------------- / memory type + +impl VulkanCore { + fn memory_type( + &self, + type_bits: u32, + required_flags: vk::MemoryPropertyFlags, + ) -> u32 { + let props = unsafe { + self.instance + .get_physical_device_memory_properties(self.physical_device) + }; + props.memory_types[..] + .iter() + .enumerate() + .find(|(i, mt)| { + (type_bits & (1 << i)) != 0 + && mt.property_flags.contains(required_flags) + }) + .map(|(i, _)| i as u32) + .expect("No suitable memory type found") + } } -// --------------------------------------------------------------------- / staging buffer +// --------------------------------------------------------------------- / image barrier + +impl VulkanCore { + fn image_barrier( + &self, + command_buffer: vk::CommandBuffer, + image: vk::Image, + old_layout: vk::ImageLayout, + new_layout: vk::ImageLayout, + src_access: vk::AccessFlags, + dst_access: vk::AccessFlags, + src_stage: vk::PipelineStageFlags, + dst_stage: vk::PipelineStageFlags, + ) { + let barrier = vk::ImageMemoryBarrier::default() + .image(image) + .old_layout(old_layout) + .new_layout(new_layout) + .src_access_mask(src_access) + .dst_access_mask(dst_access) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, level_count: 1, + base_array_layer: 0, layer_count: 1, + }); + unsafe { + self.device.cmd_pipeline_barrier( + command_buffer, + src_stage, + dst_stage, + vk::DependencyFlags::empty(), + &[], &[], + &[barrier], + ); + } + } +} -pub fn create_buffer( - instance: &ash::Instance, - device: &ash::Device, - physical_device: vk::PhysicalDevice, - data: &[u8], -) -> Result<(vk::Buffer, vk::DeviceMemory), Box> { - - // configure and create buffer - let size = data.len() as u64; - let buffer_info = vk::BufferCreateInfo::default() - .size(size) - .usage(vk::BufferUsageFlags::TRANSFER_SRC) // use as source for copy operations - .sharing_mode(vk::SharingMode::EXCLUSIVE); - let buffer = unsafe { device.create_buffer(&buffer_info, None)? }; - - // configure memory type - let mem_requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; - let mem_properties = unsafe { instance.get_physical_device_memory_properties(physical_device) }; - - let memory_type_index = mem_properties.memory_types[..] - .iter() - .enumerate() - .find(|(i, mt)| { - mem_requirements.memory_type_bits & (1 << i) != 0 - && mt.property_flags.contains( - vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, - ) - }) - .map(|(i, _)| i as u32) - .expect("No suitable memory type found for staging buffer"); - - // allocate memory - let alloc_info = vk::MemoryAllocateInfo::default() - .allocation_size(mem_requirements.size) - .memory_type_index(memory_type_index); - let memory = unsafe { device.allocate_memory(&alloc_info, None)? }; - - // attach memory to buffer - unsafe { device.bind_buffer_memory(buffer, memory, 0) }?; - - // map memory and copy data - let ptr = unsafe { - device.map_memory(memory, 0, size, vk::MemoryMapFlags::empty())? - } as *mut u8; - unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), ptr, data.len()); } - unsafe { device.unmap_memory(memory) }; - - Ok((buffer, memory)) + +// --------------------------------------------------------------------- / fill background + +impl VulkanCore { + fn fill_background( + &self, + command_buffer: vk::CommandBuffer, + target_image: vk::Image, + background: (vk::Image, u32, u32), + layer_width: u32, + layer_height: u32, + ) { + let (bg_image, bg_w, bg_h) = background; + + let bg_aspect = bg_w as f64 / bg_h as f64; + let scr_aspect = layer_width as f64 / layer_height as f64; + let (bg_sx, bg_sy, bg_sw, bg_sh) = if bg_aspect > scr_aspect { + let new_w = (bg_h as f64 * scr_aspect) as u32; + let x = (bg_w - new_w) / 2; + (x, 0, new_w, bg_h) + } else { + let new_h = (bg_w as f64 / scr_aspect) as u32; + let y = (bg_h - new_h) / 2; + (0, y, bg_w, new_h) + }; + + self.image_barrier( + command_buffer, + bg_image, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + vk::AccessFlags::SHADER_READ, + vk::AccessFlags::TRANSFER_READ, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + ); + + let bg_blit = vk::ImageBlit::default() + .src_subresource(vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, base_array_layer: 0, layer_count: 1, + }) + .src_offsets([ + vk::Offset3D { x: bg_sx as i32, y: bg_sy as i32, z: 0 }, + vk::Offset3D { x: (bg_sx + bg_sw) as i32, y: (bg_sy + bg_sh) as i32, z: 1 }, + ]) + .dst_subresource(vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, base_array_layer: 0, layer_count: 1, + }) + .dst_offsets([ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { x: layer_width as i32, y: layer_height as i32, z: 1 }, + ]); + unsafe { + self.device.cmd_blit_image( + command_buffer, + bg_image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + target_image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[bg_blit], + vk::Filter::LINEAR, + ); + } + } } -// --------------------------------------------------------------------- / allocate vram +// --------------------------------------------------------------------- / create buffer -pub fn create_texture( - instance: &ash::Instance, - device: &ash::Device, - physical_device: vk::PhysicalDevice, - width: u32, - height: u32, - usage: vk::ImageUsageFlags, - format: vk::Format, -) -> Result<(vk::Image, vk::DeviceMemory), Box> { - - // describe the image - let image_info = vk::ImageCreateInfo::default() - .image_type(vk::ImageType::TYPE_2D) - .format(format) - .extent(vk::Extent3D { width, height, depth: 1 }) - .mip_levels(1) - .array_layers(1) - .samples(vk::SampleCountFlags::TYPE_1) - .tiling(vk::ImageTiling::OPTIMAL) - .usage(usage) - .sharing_mode(vk::SharingMode::EXCLUSIVE) - .initial_layout(vk::ImageLayout::UNDEFINED); - - // create image handle - let image = unsafe { device.create_image(&image_info, None)? }; - - // how much memory does the image need? - let mem_requirements = unsafe { device.get_image_memory_requirements(image) }; - let mem_properties = unsafe { instance.get_physical_device_memory_properties(physical_device) }; - - // choose the memory type - let memory_type_index = mem_properties.memory_types[..] - .iter() - .enumerate() - .find(|(i, mt)| { - mem_requirements.memory_type_bits & (1 << i) != 0 - && mt.property_flags.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL) - }) - .map(|(i, _)| i as u32) - .expect("No suitable memory type for texture image"); +impl VulkanCore { + pub fn create_buffer( + &self, + data: &[u8], + ) -> Result<(vk::Buffer, vk::DeviceMemory), Box> { + let size = data.len() as u64; + let buffer_info = vk::BufferCreateInfo::default() + .size(size) + .usage(vk::BufferUsageFlags::TRANSFER_SRC) + .sharing_mode(vk::SharingMode::EXCLUSIVE); - // allocate vram for the image - let alloc_info = vk::MemoryAllocateInfo::default() - .allocation_size(mem_requirements.size) - .memory_type_index(memory_type_index); - let memory = unsafe { device.allocate_memory(&alloc_info, None)? }; + let buffer = unsafe { self.device.create_buffer(&buffer_info, None)? }; + let mem_requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) }; - // attach the vram to the image - unsafe { device.bind_image_memory(image, memory, 0) }?; + let memory_type_index = self.memory_type( + mem_requirements.memory_type_bits, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + ); - Ok((image, memory)) -} + let alloc_info = vk::MemoryAllocateInfo::default() + .allocation_size(mem_requirements.size) + .memory_type_index(memory_type_index); + let memory = unsafe { self.device.allocate_memory(&alloc_info, None)? }; + unsafe { self.device.bind_buffer_memory(buffer, memory, 0) }?; -// --------------------------------------------------------------------- / write texture + let ptr = unsafe { + self.device.map_memory(memory, 0, size, vk::MemoryMapFlags::empty())? + } as *mut u8; + unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), ptr, data.len()) }; + unsafe { self.device.unmap_memory(memory) }; -pub fn load_texture( - device: &ash::Device, - graphics_queue: vk::Queue, - command_pool: vk::CommandPool, - command_buffer: vk::CommandBuffer, - buffer: vk::Buffer, - image: vk::Image, - width: u32, - height: u32, -) -> Result<(), Box> { - - // reuse the persistent command buffer - unsafe { device.reset_command_pool(command_pool, vk::CommandPoolResetFlags::empty()) }?; - - // begin recording commands - let begin_info = vk::CommandBufferBeginInfo::default() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); - unsafe { device.begin_command_buffer(command_buffer, &begin_info) }?; - - // make the texture writable - let barrier1 = vk::ImageMemoryBarrier::default() - .image(image) - .old_layout(vk::ImageLayout::UNDEFINED) - .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) - .src_access_mask(vk::AccessFlags::empty()) - .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - unsafe { - device.cmd_pipeline_barrier( - command_buffer, - vk::PipelineStageFlags::TOP_OF_PIPE, - vk::PipelineStageFlags::TRANSFER, - vk::DependencyFlags::empty(), - &[], - &[], - &[barrier1], + Ok((buffer, memory)) + } +} + + +// --------------------------------------------------------------------- / create texture + +impl VulkanCore { + pub fn create_texture( + &self, + width: u32, + height: u32, + usage: vk::ImageUsageFlags, + format: vk::Format, + ) -> Result<(vk::Image, vk::DeviceMemory), Box> { + let image_info = vk::ImageCreateInfo::default() + .image_type(vk::ImageType::TYPE_2D) + .format(format) + .extent(vk::Extent3D { width, height, depth: 1 }) + .mip_levels(1) + .array_layers(1) + .samples(vk::SampleCountFlags::TYPE_1) + .tiling(vk::ImageTiling::OPTIMAL) + .usage(usage) + .sharing_mode(vk::SharingMode::EXCLUSIVE) + .initial_layout(vk::ImageLayout::UNDEFINED); + + let image = unsafe { self.device.create_image(&image_info, None)? }; + let mem_requirements = unsafe { self.device.get_image_memory_requirements(image) }; + + let memory_type_index = self.memory_type( + mem_requirements.memory_type_bits, + vk::MemoryPropertyFlags::DEVICE_LOCAL, ); + + let alloc_info = vk::MemoryAllocateInfo::default() + .allocation_size(mem_requirements.size) + .memory_type_index(memory_type_index); + let memory = unsafe { self.device.allocate_memory(&alloc_info, None)? }; + + unsafe { self.device.bind_image_memory(image, memory, 0) }?; + + Ok((image, memory)) } +} - // copy the buffer into the image - let region = vk::BufferImageCopy::default() - .buffer_offset(0) - .buffer_row_length(0) - .buffer_image_height(0) - .image_subresource(vk::ImageSubresourceLayers { - aspect_mask: vk::ImageAspectFlags::COLOR, - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }) - .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, - buffer, + +// --------------------------------------------------------------------- / load texture + +impl VulkanCore { + fn load_texture( + &self, + buffer: vk::Buffer, + image: vk::Image, + width: u32, + height: u32, + ) -> Result<(), Box> { + unsafe { + self.device + .reset_command_pool(self.command_pool, vk::CommandPoolResetFlags::empty())?; + } + + let begin_info = vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); + unsafe { + self.device + .begin_command_buffer(self.command_buffer, &begin_info)?; + } + + // transition texture to transfer dst + self.image_barrier( + self.command_buffer, image, + vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &[region], + vk::AccessFlags::empty(), + vk::AccessFlags::TRANSFER_WRITE, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, ); - } - // make the texture readable by shaders - let barrier2 = vk::ImageMemoryBarrier::default() - .image(image) - .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(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - unsafe { - device.cmd_pipeline_barrier( - command_buffer, + // copy buffer to image + let region = vk::BufferImageCopy::default() + .buffer_offset(0) + .buffer_row_length(0) + .buffer_image_height(0) + .image_subresource(vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }) + .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 }) + .image_extent(vk::Extent3D { width, height, depth: 1 }); + unsafe { + self.device.cmd_copy_buffer_to_image( + self.command_buffer, + buffer, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[region], + ); + } + + // transition texture to shader read only + self.image_barrier( + self.command_buffer, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + vk::AccessFlags::TRANSFER_WRITE, + vk::AccessFlags::SHADER_READ, vk::PipelineStageFlags::TRANSFER, vk::PipelineStageFlags::FRAGMENT_SHADER, - vk::DependencyFlags::empty(), - &[], - &[], - &[barrier2], ); - } - // finish recording commands - unsafe { device.end_command_buffer(command_buffer) }?; + unsafe { + self.device.end_command_buffer(self.command_buffer)?; + } - // submit recording commands - let submit_info = vk::SubmitInfo::default() - .command_buffers(std::slice::from_ref(&command_buffer)); - let fence = unsafe { device.create_fence(&vk::FenceCreateInfo::default(), None) }?; - unsafe { device.queue_submit(graphics_queue, &[submit_info], fence) }?; - unsafe { device.wait_for_fences(&[fence], true, u64::MAX) }?; - unsafe { device.destroy_fence(fence, None) }; + let submit_info = vk::SubmitInfo::default() + .command_buffers(std::slice::from_ref(&self.command_buffer)); + let fence = unsafe { + self.device + .create_fence(&vk::FenceCreateInfo::default(), None)? + }; + unsafe { + self.device + .queue_submit(self.graphics_queue, &[submit_info], fence)?; + self.device + .wait_for_fences(&[fence], true, u64::MAX)?; + self.device.destroy_fence(fence, None); + } - Ok(()) + Ok(()) + } } -// --------------------------------------------------------------------- / draw wallpaper +// --------------------------------------------------------------------- / upload texture -pub fn draw_wallpaper( - instance: &ash::Instance, - device: &ash::Device, - graphics_queue: vk::Queue, - command_pool: vk::CommandPool, - command_buffer: vk::CommandBuffer, - swapchain: vk::SwapchainKHR, - swapchain_images: &[vk::Image], - texture_image: vk::Image, - texture_width: u32, - texture_height: u32, - swapchain_extent_width: u32, - swapchain_extent_height: u32, - anchor_x: f32, - anchor_y: f32, - blur_bg: Option<(vk::Image, u32, u32)>, - mode: &str, -) -> Result<(), Box> { - - // acquire swapchain image - let swapchain_loader = ash::khr::swapchain::Device::new(instance, device); - let (image_index, _suboptimal) = match unsafe { - swapchain_loader.acquire_next_image( - swapchain, - u64::MAX, - vk::Semaphore::null(), - vk::Fence::null(), - ) - } { - Ok((idx, suboptimal)) => (idx, suboptimal), - Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => { - return Err("swapchain out of date (acquire)".into()); +impl VulkanCore { + pub fn upload_texture( + &self, + pixel_bytes: &[u8], + width: u32, + height: u32, + ) -> Result> { + + // copy raw image data to staging buffer + let (buffer, memory) = self.create_buffer(pixel_bytes)?; + + // allocate vram for image data + let (texture, vram) = self.create_texture( + width, + height, + vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED, + vk::Format::R8G8B8A8_SRGB, + )?; + + // load pixel data from buffer to texture + self.load_texture(buffer, texture, width, height)?; + + // drop the staging buffer (no longer needed) + unsafe { + self.device.destroy_buffer(buffer, None); + self.device.free_memory(memory, None); } - Err(e) => return Err(Box::new(e)), - }; - let target_image = swapchain_images[image_index as usize]; - - // reuse the persistent command buffer - unsafe { device.reset_command_pool(command_pool, vk::CommandPoolResetFlags::empty()) }?; - - // begin recording commands - let begin_info = vk::CommandBufferBeginInfo::default() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); - unsafe { device.begin_command_buffer(command_buffer, &begin_info) }?; - - // transition texture read-only access to source image transfer command - let texture_barrier = vk::ImageMemoryBarrier::default() - .image(texture_image) - .old_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL) - .src_access_mask(vk::AccessFlags::SHADER_READ) - .dst_access_mask(vk::AccessFlags::TRANSFER_READ) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - - // transition swapchain unknown layout to destination image transfer command - let swapchain_barrier = vk::ImageMemoryBarrier::default() - .image(target_image) - .old_layout(vk::ImageLayout::UNDEFINED) - .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) - .src_access_mask(vk::AccessFlags::empty()) - .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - - // execute both source and destination barriers - unsafe { - device.cmd_pipeline_barrier( - command_buffer, - vk::PipelineStageFlags::TOP_OF_PIPE, - vk::PipelineStageFlags::TRANSFER, - vk::DependencyFlags::empty(), - &[], - &[], - &[texture_barrier, swapchain_barrier], - ); + + Ok(VulkanTexture { + image: texture, + _memory: vram, + width, + height, + }) } +} - // compute source and destination rectangles based on mode - let (src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h, needs_clear) = mode_set( - texture_width, texture_height, - swapchain_extent_width, swapchain_extent_height, - anchor_x, anchor_y, - mode, - ); - // if the mode requires background, fill with blurred wallpaper or black - if needs_clear { - match blur_bg { - Some((bg_image, bg_w, bg_h)) => { - // Compute a cover-crop rectangle for the blurred background - let bg_aspect = bg_w as f64 / bg_h as f64; - let scr_aspect = swapchain_extent_width as f64 / swapchain_extent_height as f64; - let (bg_sx, bg_sy, bg_sw, bg_sh) = if bg_aspect > scr_aspect { - // background wider → crop left/right (centered) - let new_w = (bg_h as f64 * scr_aspect) as u32; - let x = (bg_w - new_w) / 2; - (x, 0, new_w, bg_h) - } else { - // background taller → crop top/bottom (centered) - let new_h = (bg_w as f64 / scr_aspect) as u32; - let y = (bg_h - new_h) / 2; - (0, y, bg_w, new_h) - }; - - // Transition bg image to TRANSFER_SRC_OPTIMAL - let bg_barrier = vk::ImageMemoryBarrier::default() - .image(bg_image) - .old_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .new_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL) - .src_access_mask(vk::AccessFlags::SHADER_READ) - .dst_access_mask(vk::AccessFlags::TRANSFER_READ) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, level_count: 1, - base_array_layer: 0, layer_count: 1, - }); - unsafe { - device.cmd_pipeline_barrier( - command_buffer, - vk::PipelineStageFlags::TOP_OF_PIPE, - vk::PipelineStageFlags::TRANSFER, - vk::DependencyFlags::empty(), - &[], &[], &[bg_barrier], +// --------------------------------------------------------------------- / draw wallpaper + +impl VulkanCore { + pub fn draw_wallpaper( + &self, + surfchain: &VulkanSurfchain, + texture: &VulkanTexture, + layer_width: u32, + layer_height: u32, + anchor_x: f32, + anchor_y: f32, + background: Option<(vk::Image, u32, u32)>, + mode: &str, + ) -> Result<(), Box> { + let swapchain_loader = ash::khr::swapchain::Device::new(&self.instance, &self.device); + let (image_index, _suboptimal) = match unsafe { + swapchain_loader.acquire_next_image( + surfchain.swapchain, + u64::MAX, + vk::Semaphore::null(), + vk::Fence::null(), + ) + } { + Ok((idx, suboptimal)) => (idx, suboptimal), + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => { + return Err("swapchain out of date (acquire)".into()); + } + Err(e) => return Err(Box::new(e)), + }; + let target_image = surfchain.swapchain_images[image_index as usize]; + + self.record_commands(|command_buffer| { + + // transition texture to transfer source + self.image_barrier( + command_buffer, + texture.image, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + vk::AccessFlags::SHADER_READ, + vk::AccessFlags::TRANSFER_READ, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + ); + + // transition swapchain image to transfer destination + self.image_barrier( + command_buffer, + target_image, + vk::ImageLayout::UNDEFINED, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + vk::AccessFlags::empty(), + vk::AccessFlags::TRANSFER_WRITE, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + ); + + // compute source and destination rectangles based on mode + let (src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h, needs_clear) = + mode_set( + texture.width, texture.height, + layer_width, layer_height, + anchor_x, anchor_y, + mode, ); + + if needs_clear { + let bg = background.expect("background must exist for fit/original mode"); + self.fill_background(command_buffer, target_image, bg, layer_width, layer_height); } - // Blit the cropped background to cover the entire screen - let bg_blit = vk::ImageBlit::default() + // blit main wallpaper + let blit_region = vk::ImageBlit::default() .src_subresource(vk::ImageSubresourceLayers { aspect_mask: vk::ImageAspectFlags::COLOR, - mip_level: 0, - base_array_layer: 0, - layer_count: 1, + mip_level: 0, base_array_layer: 0, layer_count: 1, }) .src_offsets([ - vk::Offset3D { x: bg_sx as i32, y: bg_sy as i32, z: 0 }, - vk::Offset3D { x: (bg_sx + bg_sw) as i32, y: (bg_sy + bg_sh) as i32, z: 1 }, + vk::Offset3D { x: src_x as i32, y: src_y as i32, z: 0 }, + vk::Offset3D { x: (src_x + src_w) as i32, y: (src_y + src_h) as i32, z: 1 }, ]) .dst_subresource(vk::ImageSubresourceLayers { aspect_mask: vk::ImageAspectFlags::COLOR, - mip_level: 0, - base_array_layer: 0, - layer_count: 1, + mip_level: 0, base_array_layer: 0, layer_count: 1, }) .dst_offsets([ - vk::Offset3D { x: 0, y: 0, z: 0 }, - vk::Offset3D { x: swapchain_extent_width as i32, y: swapchain_extent_height as i32, z: 1 }, + vk::Offset3D { x: dst_x, y: dst_y, z: 0 }, + vk::Offset3D { x: dst_x + dst_w as i32, y: dst_y + dst_h as i32, z: 1 }, ]); unsafe { - device.cmd_blit_image( + self.device.cmd_blit_image( command_buffer, - bg_image, + texture.image, vk::ImageLayout::TRANSFER_SRC_OPTIMAL, target_image, vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &[bg_blit], + &[blit_region], vk::Filter::LINEAR, ); } + + // transition swapchain image to present layout + self.image_barrier( + command_buffer, + target_image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + vk::ImageLayout::PRESENT_SRC_KHR, + vk::AccessFlags::TRANSFER_WRITE, + vk::AccessFlags::MEMORY_READ, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + ); + })?; + + // present the final image + let present_info = vk::PresentInfoKHR::default() + .swapchains(std::slice::from_ref(&surfchain.swapchain)) + .image_indices(std::slice::from_ref(&image_index)); + + let result = unsafe { swapchain_loader.queue_present(self.graphics_queue, &present_info) }; + match result { + Ok(_) => Ok(()), + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => { + Err("swapchain out of date (present)".into()) } - None => { - // fallback to solid black - let clear_color = vk::ClearColorValue { float32: [0.0, 0.0, 0.0, 1.0] }; - let clear_range = vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, level_count: 1, - base_array_layer: 0, layer_count: 1, - }; - unsafe { - device.cmd_clear_color_image( - command_buffer, - target_image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &clear_color, - &[clear_range], - ); - } - } + Err(vk::Result::SUBOPTIMAL_KHR) => Ok(()), + Err(e) => Err(Box::new(e)), } } - - // record the blit command - let blit_region = vk::ImageBlit::default() - .src_subresource(vk::ImageSubresourceLayers { - aspect_mask: vk::ImageAspectFlags::COLOR, - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }) - .src_offsets([ - vk::Offset3D { - x: src_x as i32, - y: src_y as i32, - z: 0, - }, - vk::Offset3D { - x: (src_x + src_w) as i32, - y: (src_y + src_h) as i32, - z: 1, - }, - ]) - .dst_subresource(vk::ImageSubresourceLayers { - aspect_mask: vk::ImageAspectFlags::COLOR, - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }) - .dst_offsets([ - vk::Offset3D { x: dst_x, y: dst_y, z: 0 }, - vk::Offset3D { x: dst_x + dst_w as i32, y: dst_y + dst_h as i32, z: 1 }, - ]); - unsafe { - device.cmd_blit_image( - command_buffer, - texture_image, - vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - target_image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &[blit_region], - vk::Filter::LINEAR, - ); - } - - // transition swapchain to readable layout for compositor - let present_barrier = vk::ImageMemoryBarrier::default() - .image(target_image) - .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) - .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) - .src_access_mask(vk::AccessFlags::TRANSFER_WRITE) - .dst_access_mask(vk::AccessFlags::MEMORY_READ) - .subresource_range(vk::ImageSubresourceRange { - aspect_mask: vk::ImageAspectFlags::COLOR, - base_mip_level: 0, - level_count: 1, - base_array_layer: 0, - layer_count: 1, - }); - unsafe { - device.cmd_pipeline_barrier( - command_buffer, - vk::PipelineStageFlags::TRANSFER, - vk::PipelineStageFlags::BOTTOM_OF_PIPE, - vk::DependencyFlags::empty(), - &[], - &[], - &[present_barrier], - ); - } - - // finish recording commands - unsafe { device.end_command_buffer(command_buffer) }?; - - // submit recording commands - let submit_info = vk::SubmitInfo::default() - .command_buffers(std::slice::from_ref(&command_buffer)); - let fence = unsafe { device.create_fence(&vk::FenceCreateInfo::default(), None) }?; - unsafe { device.queue_submit(graphics_queue, &[submit_info], fence) }?; - unsafe { device.wait_for_fences(&[fence], true, u64::MAX) }?; - unsafe { device.destroy_fence(fence, None) }; - - // present the image - let present_info = vk::PresentInfoKHR::default() - .swapchains(std::slice::from_ref(&swapchain)) - .image_indices(std::slice::from_ref(&image_index)); - - let result = unsafe { swapchain_loader.queue_present(graphics_queue, &present_info) }; - match result { - Ok(_) => return Ok(()), - Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => {return Err("swapchain out of date (present)".into())} - Err(vk::Result::SUBOPTIMAL_KHR) => return Ok(()), - Err(e) => return Err(Box::new(e)), - } } @@ -812,7 +770,6 @@ fn mode_set( anchor_y: f32, mode: &str, ) -> (u32, u32, u32, u32, i32, i32, u32, u32, bool) { - if mode == "fit" { let scale = (scr_w as f64 / img_w as f64).min(scr_h as f64 / img_h as f64); let sw = (img_w as f64 * scale) as u32; @@ -845,7 +802,7 @@ fn mode_set( } -// --------------------------------------------------------------------- / destroy level +// --------------------------------------------------------------------- / destroy core pub fn destroy_wallbash( vk_core: &VulkanCore, @@ -856,7 +813,7 @@ pub fn destroy_wallbash( level: u32, ) { - // Destroy filter resources if provided and level ≥ 0 + // destroy filter resources if provided and level ≥ 0 if let (Some(module), Some(pipeline), Some(desc)) = (filter_module, filter_pipeline, filter_desc_layout) { unsafe { vk_core.device.destroy_pipeline(pipeline, None); @@ -865,7 +822,7 @@ pub fn destroy_wallbash( } } - // Destroy swapchain and surface (level ≥ 1) + // destroy swapchain and surface (level ≥ 1) if level >= 1 { if let Some(sc) = surfchain { unsafe { @@ -877,7 +834,7 @@ pub fn destroy_wallbash( } } - // Destroy core Vulkan objects (level ≥ 2) + // destroy core Vulkan objects (level ≥ 2) if level >= 2 { unsafe { vk_core.device.device_wait_idle().expect("device wait failed"); diff --git a/src/wallbash.rs b/src/wallbash.rs index 4210c2e..3b59be9 100644 --- a/src/wallbash.rs +++ b/src/wallbash.rs @@ -74,12 +74,7 @@ fn set_wallpaper( let pixel_bytes = pixel_format.into_raw(); // call the vulkan pipeline - let texture = vulkan::vulkan_pipeline( - vk_core, - &pixel_bytes, - img.width(), - img.height(), - )?; + let texture = vk_core.upload_texture(&pixel_bytes, img.width(), img.height(),)?; // drop the old texture resources (if any) if let Some(old_tex) = wallpaper.take() { @@ -95,17 +90,9 @@ fn set_wallpaper( let background = background_texture.as_ref().map(|b| (b.image, b.width, b.height)); // draw the wallpaper - vulkan::draw_wallpaper( - &vk_core.instance, - &vk_core.device, - vk_core.graphics_queue, - vk_core.command_pool, - vk_core.command_buffer, - vk_surfchain.swapchain, - &vk_surfchain.swapchain_images, - wallpaper.as_ref().unwrap().image, - img.width(), - img.height(), + vk_core.draw_wallpaper( + vk_surfchain, + wallpaper.as_ref().unwrap(), layer_width, layer_height, anchor_x,