vulkan_lib/vulkan-rs/src/image.rs
2023-01-14 13:03:01 +01:00

1163 lines
36 KiB
Rust

use assetpath::AssetPath;
use crate::prelude::*;
use anyhow::Result;
use std::cmp;
use std::sync::{Arc, Mutex};
use std::time::Duration;
enum ImageSourceType {
Empty,
Raw(Vec<u8>),
Array(Vec<Arc<Image>>),
}
struct ImageCreateInfo {
vk_image_create_info: VkImageCreateInfo,
source_type: ImageSourceType,
}
impl ImageCreateInfo {
fn default(source_type: ImageSourceType) -> Self {
ImageCreateInfo {
vk_image_create_info: VkImageCreateInfo::new(
0,
VK_IMAGE_TYPE_2D,
VK_FORMAT_UNDEFINED,
VkExtent3D {
width: 0,
height: 0,
depth: 0,
},
1,
1,
VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_SHARING_MODE_EXCLUSIVE,
&[],
VK_IMAGE_LAYOUT_UNDEFINED,
),
source_type,
}
}
}
struct PreinitializedImage {
image: VkImage,
format: VkFormat,
width: u32,
height: u32,
layers: u32,
sample_count: VkSampleCountFlagBits,
layout: VkImageLayout,
usage: VkImageUsageFlagBits,
assume_layout: bool,
}
enum ImageBuilderInternalType {
PreinitializedImage(PreinitializedImage),
NewImage(ImageCreateInfo),
}
/// Implements the builder pattern for Image
pub struct ImageBuilder {
file_name: Option<AssetPath>,
builder_type: ImageBuilderInternalType,
components: VkComponentMapping,
view_type: VkImageViewType,
subresource_range: VkImageSubresourceRange,
sampler: Option<Arc<Sampler>>,
}
impl ImageBuilder {
/// Sets up the ImageBuilder for further use
fn new(internal_type: ImageBuilderInternalType) -> Self {
ImageBuilder {
file_name: None,
builder_type: internal_type,
components: VkComponentMapping::default(),
subresource_range: VkImageSubresourceRange {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
baseMipLevel: 0,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
},
view_type: VK_IMAGE_VIEW_TYPE_2D,
sampler: None,
}
}
pub fn build(self, device: &Arc<Device>, queue: &Arc<Mutex<Queue>>) -> Result<Arc<Image>> {
let mut image_view_ci = self.vk_image_view_create_info();
match self.builder_type {
ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => {
image_view_ci.image = preinitialized_image.image;
let image_view = device.create_image_view(&image_view_ci)?;
let image = Arc::new(Image {
device: device.clone(),
queue: queue.clone(),
image: preinitialized_image.image,
image_view,
_memory: None,
attached: true,
sampler: self.sampler,
file_name: self.file_name,
format: preinitialized_image.format,
image_layout: Mutex::new(VK_IMAGE_LAYOUT_UNDEFINED),
aspect_mask: self.subresource_range.aspectMask,
width: preinitialized_image.width,
height: preinitialized_image.height,
layers: preinitialized_image.layers,
levels: 1,
sample_count: preinitialized_image.sample_count,
_usage: preinitialized_image.usage,
});
// TODO: check necessity
if !preinitialized_image.assume_layout {
if preinitialized_image.layout != VK_IMAGE_LAYOUT_UNDEFINED {
Image::convert_layout(&image, preinitialized_image.layout)?;
}
} else {
*image.image_layout.lock().unwrap() = preinitialized_image.layout;
}
Ok(image)
}
ImageBuilderInternalType::NewImage(ref info) => match info.source_type {
ImageSourceType::Array(ref array) => {
let arc_image = Self::create_from_source(
device,
queue,
info,
self.sampler,
image_view_ci,
self.file_name,
)?;
copy_images_to_imagearray(device, queue, &arc_image, array)?;
Ok(arc_image)
}
ImageSourceType::Raw(ref raw) => {
let arc_image = Self::create_from_source(
device,
queue,
info,
self.sampler,
image_view_ci,
self.file_name,
)?;
Self::optimize_fill(device, queue, raw, &arc_image)?;
Ok(arc_image)
}
ImageSourceType::Empty => {
let arc_image = Self::create_from_source(
device,
queue,
info,
self.sampler,
image_view_ci,
self.file_name,
)?;
Ok(arc_image)
}
},
}
}
pub fn check_configuration(&self, device: &Arc<Device>) -> bool {
match &self.builder_type {
ImageBuilderInternalType::NewImage(create_info) => Image::check_configuration(
device,
create_info.vk_image_create_info.tiling,
create_info.vk_image_create_info.format,
create_info.vk_image_create_info.usage,
),
_ => false,
}
}
pub fn view_type(mut self, view_type: VkImageViewType) -> Self {
self.view_type = view_type;
self
}
pub fn component_swizzle(
mut self,
r: VkComponentSwizzle,
g: VkComponentSwizzle,
b: VkComponentSwizzle,
a: VkComponentSwizzle,
) -> Self {
self.components.r = r;
self.components.g = g;
self.components.b = b;
self.components.a = a;
self
}
pub fn update_data(mut self, data: Vec<u8>) -> Self {
match self.builder_type {
ImageBuilderInternalType::NewImage(ref mut info) => match info.source_type {
ImageSourceType::Raw(ref mut old_data) => *old_data = data,
_ => panic!("wrong source type in ImageBuilder"),
},
_ => panic!("wrong builder type in ImageBuilder"),
}
self
}
pub fn format(mut self, format: VkFormat) -> Self {
match &mut self.builder_type {
ImageBuilderInternalType::NewImage(info) => {
info.vk_image_create_info.format = format;
}
ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => {
preinitialized_image.format = format;
}
}
self
}
pub fn sample_count(mut self, sample_count: impl Into<VkSampleCountFlagBits>) -> Self {
match &mut self.builder_type {
ImageBuilderInternalType::NewImage(info) => {
info.vk_image_create_info.samples = sample_count.into();
}
ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => {
preinitialized_image.sample_count = sample_count.into();
}
}
self
}
pub fn add_usage<T>(mut self, usage: T) -> Self
where
T: Into<VkImageUsageFlagBits>,
{
match self.builder_type {
ImageBuilderInternalType::NewImage(ref mut info) => {
info.vk_image_create_info.usage |= usage.into();
}
_ => panic!("wrong builder type in ImageBuilder"),
}
self
}
pub fn flags<T>(mut self, flags: T) -> Self
where
T: Into<VkImageCreateFlagBits>,
{
match self.builder_type {
ImageBuilderInternalType::NewImage(ref mut info) => {
info.vk_image_create_info.flags = flags.into();
}
_ => panic!("wrong builder type in ImageBuilder"),
}
self
}
pub fn array_layers(mut self, layers: u32) -> Self {
match &mut self.builder_type {
ImageBuilderInternalType::NewImage(info) => {
info.vk_image_create_info.arrayLayers = layers;
self.subresource_range.layerCount = layers;
}
ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => {
preinitialized_image.layers = layers;
}
}
self
}
pub fn attach_sampler(mut self, sampler: Arc<Sampler>) -> Self {
self.sampler = Some(sampler);
if let ImageBuilderInternalType::NewImage(ref mut info) = self.builder_type {
info.vk_image_create_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
self
}
// pub fn mip_map_levels(mut self, levels: u32) -> Self {
// match self.builder_type {
// ImageBuilderInternalType::NewImage(ref mut info) => {
// info.vk_image_create_info.mipLevels = levels;
// self.subresource_range.levelCount = levels;
// info.vk_image_create_info.usage |=
// VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
// if let Some(ref mut sampler) = self.sampler_info {
// sampler.maxLod = levels as f32;
// }
// }
// _ => panic!("wrong builder type in ImageBuilder"),
// }
// self
// }
pub fn max_mip_map_levels(mut self) -> Self {
match self.builder_type {
ImageBuilderInternalType::NewImage(ref mut info) => {
let levels = Self::calc_mip_map_levels(
info.vk_image_create_info.extent.width,
info.vk_image_create_info.extent.height,
);
info.vk_image_create_info.mipLevels = levels;
self.subresource_range.levelCount = levels;
info.vk_image_create_info.usage |=
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
_ => panic!("wrong builder type in ImageBuilder"),
}
self
}
pub fn aspect_mask(mut self, mask: VkImageAspectFlags) -> Self {
self.subresource_range.aspectMask = mask.into();
self
}
fn calc_mip_map_levels(width: u32, height: u32) -> u32 {
1 + (cmp::max(width, height) as f32).log2().floor() as u32
}
fn vk_image_view_create_info(&self) -> VkImageViewCreateInfo {
VkImageViewCreateInfo::new(
0,
VkImage::NULL_HANDLE,
self.view_type,
match &self.builder_type {
ImageBuilderInternalType::NewImage(info) => info.vk_image_create_info.format,
ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => {
preinitialized_image.format
}
},
self.components.clone(),
self.subresource_range.clone(),
)
}
fn create_from_source(
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
info: &ImageCreateInfo,
sampler: Option<Arc<Sampler>>,
mut view_ci: VkImageViewCreateInfo,
file_name: Option<AssetPath>,
) -> Result<Arc<Image>> {
let format = view_ci.format;
let (image, memory) = Self::create_texture(device, &info.vk_image_create_info)?;
view_ci.image = image;
let image_view = device.create_image_view(&view_ci)?;
Ok(Arc::new(Image {
device: device.clone(),
queue: queue.clone(),
image,
image_view,
attached: false,
_memory: Some(memory),
sampler,
file_name,
format,
image_layout: Mutex::new(info.vk_image_create_info.initialLayout),
aspect_mask: view_ci.subresourceRange.aspectMask,
width: info.vk_image_create_info.extent.width,
height: info.vk_image_create_info.extent.height,
layers: info.vk_image_create_info.arrayLayers,
levels: info.vk_image_create_info.mipLevels,
sample_count: info.vk_image_create_info.samples,
_usage: info.vk_image_create_info.usage,
}))
}
fn create_texture(
device: &Arc<Device>,
image_ci: &VkImageCreateInfo,
) -> Result<(VkImage, Arc<Memory<u8>>)> {
let image = Self::create_image(device, image_ci)?;
let memory = Memory::image_memory(
device,
image,
MemoryUsage::into_vma(Some(MemoryUsage::GpuOnly)),
)?;
Ok((image, memory))
}
fn create_image(device: &Arc<Device>, image_ci: &VkImageCreateInfo) -> Result<VkImage> {
device.create_image(image_ci)
}
fn optimize_fill(
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
data: &[u8],
image: &Arc<Image>,
) -> Result<()> {
let staging_buffer = Buffer::builder()
.set_usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT)
.set_memory_usage(MemoryUsage::CpuToGpu)
.set_data(data)
.build(device.clone())?;
copy_buffer_to_image(device, queue, &staging_buffer, image)?;
Ok(())
}
}
/// Wrapper type around VkImage
///
/// handles VkImage, VkSampler, VkDeviceSize and VkImageView internally
/// just as you set it up to
#[derive(Debug)]
pub struct Image {
// device handle
device: Arc<Device>,
// queue handle
queue: Arc<Mutex<Queue>>,
file_name: Option<AssetPath>,
// image handle
attached: bool,
image: VkImage,
// image_view
image_view: VkImageView,
// optional handles
_memory: Option<Arc<Memory<u8>>>,
sampler: Option<Arc<Sampler>>,
// image information
format: VkFormat,
image_layout: Mutex<VkImageLayout>,
aspect_mask: VkImageAspectFlagBits,
width: u32,
height: u32,
layers: u32, // array layers
levels: u32, // mip map levels
sample_count: VkSampleCountFlagBits,
_usage: VkImageUsageFlagBits,
}
impl Image {
/// Creates an `ImageBuilder` where you can define the image for your needs
///
/// For example, this is used to wrap swapchain images
///
/// # Arguments
///
/// * `image` - valid VkImage handle
/// * `format` - format of this image
pub fn from_preinitialized(
image: VkImage,
format: VkFormat,
width: u32,
height: u32,
layout: VkImageLayout,
usage: impl Into<VkImageUsageFlagBits>,
assume_layout: bool,
) -> ImageBuilder {
ImageBuilder::new(ImageBuilderInternalType::PreinitializedImage(
PreinitializedImage {
image,
format,
width,
height,
layers: 1,
sample_count: VK_SAMPLE_COUNT_1_BIT.into(),
layout,
usage: usage.into(),
assume_layout,
},
))
}
/// Creates an `ImageBuilder` where you can define the image for your needs
///
/// takes the image data in form of an Vec<u8> and sets up the `ImageBuilder`
/// for further configuration
///
/// # Arguments
///
/// * `source` - The color information for the image
/// * `width` - The target width of the image
/// * `height` - The target height of the image
pub fn from_raw(source: Vec<u8>, width: u32, height: u32) -> ImageBuilder {
let mut create_info = ImageCreateInfo::default(ImageSourceType::Raw(source));
create_info.vk_image_create_info.extent.width = width;
create_info.vk_image_create_info.extent.height = height;
create_info.vk_image_create_info.extent.depth = 1;
create_info.vk_image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
ImageBuilder::new(ImageBuilderInternalType::NewImage(create_info))
}
/// Creates an `ImageBuilder` where you can define the image for your needs
///
/// takes a path to the file and does the same as `raw_source`, but it
/// extracts all needed bits from the file
///
/// # Arguments
///
/// * `file` - The path to the file
pub fn from_file(file: AssetPath) -> Result<ImageBuilder> {
let texture = match image::open(&file.full_path()) {
Ok(i) => i.to_rgba8(),
Err(err) => return Err(anyhow::Error::new(err).context(file.full_path())),
};
let (width, height) = texture.dimensions();
let mut builder =
Self::from_raw(texture.into_raw(), width, height).format(VK_FORMAT_R8G8B8A8_UNORM);
builder.file_name = Some(file);
Ok(builder)
}
/// Creates an `ImageBuilder` where you can define the image for your needs
///
/// takes a byte slice and does the same as `raw_source`, but it
/// extracts all needed bits from the slice
///
/// # Usage
///
/// let mut image_builder = Image::from_slice(include_bytes!("path/to/file"))?;
///
/// # Arguments
///
/// * `slice` - Slice of bytes
pub fn from_slice(data: &[u8]) -> Result<ImageBuilder> {
let texture = image::load_from_memory(data)?.to_rgba8();
let (width, height) = texture.dimensions();
Ok(Self::from_raw(texture.into_raw(), width, height).format(VK_FORMAT_R8G8B8A8_UNORM))
}
/// Creates an `ImageBuilder` where you can define the image for your needs
///
/// takes an array `Arc<Image>`'s and setups the `ImageBuilder` to create
/// a single `Arc<Image>` with an 2d image array created from the provided images
///
/// # Arguments
///
/// * `array` - Source images
pub fn from_array(array: Vec<Arc<Image>>) -> ImageBuilder {
debug_assert!(array.is_empty(), "images array must not be empty");
let width = array[0].width();
let height = array[0].height();
if cfg!(debug_assertions) {
for image in &array {
if width != image.width() || height != image.height() {
panic!("images are not equally sized");
}
}
}
let array_len = array.len() as u32;
let mut create_info = ImageCreateInfo::default(ImageSourceType::Array(array));
create_info.vk_image_create_info.arrayLayers = array_len;
create_info.vk_image_create_info.imageType = VK_IMAGE_TYPE_2D;
create_info.vk_image_create_info.extent.width = width;
create_info.vk_image_create_info.extent.height = height;
create_info.vk_image_create_info.extent.depth = 1;
let mut image_builder = ImageBuilder::new(ImageBuilderInternalType::NewImage(create_info));
image_builder.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
image_builder.subresource_range.layerCount = array_len;
image_builder
}
/// Creates an `ImageBuilder` where you can define the image for your needs
///
/// takes raw information to setup `ImageBuilder`, that creates an `Arc<Image>`
/// with no color information
///
/// # Arguments
///
/// * `width` - The target width of the image
/// * `height` - The target height of the image
/// * `usage` - `VkImageUsageFlagBits` mask to define the image usage
/// * `sample_count` - `VkSampleCountFlags` to define the image's sample count
pub fn empty(
width: u32,
height: u32,
usage: impl Into<VkImageUsageFlagBits>,
sample_count: VkSampleCountFlags,
) -> ImageBuilder {
let mut create_info = ImageCreateInfo::default(ImageSourceType::Empty);
create_info.vk_image_create_info.samples = sample_count.into();
create_info.vk_image_create_info.extent.width = width;
create_info.vk_image_create_info.extent.height = height;
create_info.vk_image_create_info.extent.depth = 1;
create_info.vk_image_create_info.usage = usage.into();
ImageBuilder::new(ImageBuilderInternalType::NewImage(create_info))
}
pub fn check_configuration(
device: &Arc<Device>,
tiling: VkImageTiling,
format: VkFormat,
usage: impl Into<VkImageUsageFlagBits>,
) -> bool {
let physical_device = device.physical_device();
match tiling {
VK_IMAGE_TILING_OPTIMAL => physical_device.check_optimal_format_features(format, usage),
VK_IMAGE_TILING_LINEAR => physical_device.check_linear_format_features(format, usage),
}
}
pub fn device(&self) -> &Arc<Device> {
&self.device
}
pub fn queue(&self) -> &Arc<Mutex<Queue>> {
&self.queue
}
pub fn file_name(&self) -> Option<&AssetPath> {
self.file_name.as_ref()
}
pub fn convert_layout(me: &Arc<Image>, target_layout: VkImageLayout) -> Result<()> {
into_layout(me, target_layout)
}
pub fn vk_format(&self) -> VkFormat {
self.format
}
pub fn sampler(&self) -> &Option<Arc<Sampler>> {
&self.sampler
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn layers(&self) -> u32 {
self.layers
}
pub fn levels(&self) -> u32 {
self.levels
}
pub fn sample_count(&self) -> VkSampleCountFlagBits {
self.sample_count
}
pub fn image_layout(&self) -> VkImageLayout {
*self.image_layout.lock().unwrap()
}
pub fn set_image_layout(&self, layout: VkImageLayout) {
*self.image_layout.lock().unwrap() = layout;
}
pub fn full_resource_range(&self) -> VkImageSubresourceRange {
VkImageSubresourceRange {
aspectMask: self.aspect_mask,
baseMipLevel: 0,
levelCount: self.levels,
baseArrayLayer: 0,
layerCount: self.layers,
}
}
pub fn full_resource_layers(&self) -> VkImageSubresourceLayers {
VkImageSubresourceLayers {
aspectMask: self.aspect_mask,
mipLevel: 0,
baseArrayLayer: 0,
layerCount: self.layers,
}
}
pub fn src_layout_to_access(image_layout: VkImageLayout) -> VkAccessFlagBits {
match image_layout {
VK_IMAGE_LAYOUT_UNDEFINED => 0u32.into(),
VK_IMAGE_LAYOUT_PREINITIALIZED => VK_ACCESS_HOST_WRITE_BIT.into(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL => VK_ACCESS_TRANSFER_WRITE_BIT.into(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL => VK_ACCESS_TRANSFER_READ_BIT.into(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL => VK_ACCESS_SHADER_READ_BIT.into(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL => VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT.into(),
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR => VK_ACCESS_MEMORY_READ_BIT.into(),
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL => {
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
}
VK_IMAGE_LAYOUT_GENERAL => VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
_ => unimplemented!("source image layout ({:?})", image_layout),
}
}
pub fn dst_layout_to_access(image_layout: VkImageLayout) -> VkAccessFlagBits {
match image_layout {
VK_IMAGE_LAYOUT_UNDEFINED => {
panic!("target image layout must not be VK_IMAGE_LAYOUT_UNDEFINED")
}
VK_IMAGE_LAYOUT_PREINITIALIZED => {
panic!("target image layout must not be VK_IMAGE_LAYOUT_PREINITIALIZED")
}
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL => VK_ACCESS_TRANSFER_WRITE_BIT.into(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL => VK_ACCESS_TRANSFER_READ_BIT.into(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL => VK_ACCESS_SHADER_READ_BIT.into(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL => VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT.into(),
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR => VK_ACCESS_MEMORY_READ_BIT.into(),
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL => {
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
}
VK_IMAGE_LAYOUT_GENERAL => VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL => VK_ACCESS_SHADER_READ_BIT.into(),
}
}
}
impl VulkanDevice for Image {
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl_vk_handle!(Image, VkImage, image);
impl_vk_handle!(Image, VkImageView, image_view);
impl Drop for Image {
fn drop(&mut self) {
self.device.destroy_image_view(self.image_view);
if !self.attached {
self.device.destroy_image(self.image);
}
}
}
fn into_layout(image: &Arc<Image>, layout: VkImageLayout) -> Result<()> {
// create a new command buffer
let command_buffer =
CommandBuffer::new_primary().build(image.device.clone(), image.queue.clone())?;
{
// begin recording into this command buffer
let mut buffer_recorder = command_buffer.begin(VkCommandBufferBeginInfo::new(
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
))?;
// subresource information
let subresource_range = VkImageSubresourceRange {
aspectMask: image.aspect_mask,
baseMipLevel: 0,
levelCount: image.levels(),
baseArrayLayer: 0,
layerCount: image.layers(),
};
// change image layout
buffer_recorder.set_image_layout(image, layout, subresource_range);
}
// submit current queue
let submit = SubmitInfo::default().add_command_buffer(&command_buffer);
let fence = Fence::builder().build(image.device.clone())?;
image
.queue
.lock()
.map_err(|_| anyhow::Error::msg("Failed getting vulkan device queue lock"))?
.submit(Some(&fence), &[submit])?;
image
.device
.wait_for_fences(&[&fence], true, Duration::from_secs(1))?;
Ok(())
}
fn copy_buffer_to_image<T: Send + Sync + 'static>(
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
buffer: &Arc<Buffer<T>>,
image: &Arc<Image>,
) -> Result<()>
where
T: Copy,
{
// create a new command buffer
let command_buffer = CommandBuffer::new_primary().build(device.clone(), queue.clone())?;
{
// begin recording into this command buffer
let mut buffer_recorder = command_buffer.begin(VkCommandBufferBeginInfo::new(
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
))?;
// copy info for copying the content of the buffer into the image
let buffer_image_copy = VkBufferImageCopy {
bufferOffset: 0,
bufferRowLength: 0,
bufferImageHeight: 0,
imageSubresource: VkImageSubresourceLayers {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
mipLevel: 0,
baseArrayLayer: 0,
layerCount: 1,
},
imageOffset: VkOffset3D { x: 0, y: 0, z: 0 },
imageExtent: VkExtent3D {
width: image.width(),
height: image.height(),
depth: 1,
},
};
// subresource information
let mut subresource_range = VkImageSubresourceRange {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
baseMipLevel: 0,
levelCount: image.levels(),
baseArrayLayer: 0,
layerCount: 1,
};
// set image layout to receive content
buffer_recorder.set_image_layout(
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
subresource_range.clone(),
);
// the actual copy command
buffer_recorder.copy_buffer_to_image(
buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&[buffer_image_copy],
);
// just transition one mip level at a time
subresource_range.levelCount = 1;
// mip map creation
if image.levels() > 1 {
blit_mip_maps(
&mut buffer_recorder,
image,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
);
} else {
// set image to be usable inside a shader
buffer_recorder.set_image_layout(
image,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
subresource_range,
);
}
}
// submit current queue
let submit = SubmitInfo::default().add_command_buffer(&command_buffer);
let fence = Fence::builder().build(device.clone())?;
queue
.lock()
.map_err(|_| anyhow::Error::msg("Failed getting vulkan device queue lock"))?
.submit(Some(&fence), &[submit])?;
device.wait_for_fences(&[&fence], true, Duration::from_secs(1))?;
Ok(())
}
fn copy_images_to_imagearray(
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
image_array: &Arc<Image>,
images: &[Arc<Image>],
) -> Result<()> {
// create a new command buffer
let command_buffer = CommandBuffer::new_primary().build(device.clone(), queue.clone())?;
{
// set command buffer buffer in recording state
let mut buffer_recorder = command_buffer.begin(VkCommandBufferBeginInfo::new(
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
))?;
// subresource range of the receiving image
let array_subresource_range = VkImageSubresourceRange {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
baseMipLevel: 0,
levelCount: image_array.levels(),
baseArrayLayer: 0,
layerCount: image_array.layers(),
};
// set the target image into receiving state
buffer_recorder.set_image_layout(
image_array,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
array_subresource_range.clone(),
);
for (i, image) in images.iter().enumerate() {
// if source and target image have the same count of
// mip maps or the source has more mip maps,
// we can just copy every mip level into the
// correct target level and layer
if image.levels() >= image_array.levels() {
for k in 0..image_array.levels() {
copy_image_to_image(&mut buffer_recorder, image, image_array, k, i as u32);
}
// if the source image has less mip maps than the target image,
// we just gonna copy the first level and blit the rest
} else {
copy_image_to_image(&mut buffer_recorder, image, image_array, 0, i as u32);
blit_mip_maps(
&mut buffer_recorder,
image_array,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
);
}
}
// set the target image into a shader usable state
buffer_recorder.set_image_layout(
image_array,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
array_subresource_range,
);
}
// submit current queue
let submit = SubmitInfo::default().add_command_buffer(&command_buffer);
let fence = Fence::builder().build(device.clone())?;
queue
.lock()
.map_err(|_| anyhow::Error::msg("Failed getting vulkan device queue lock"))?
.submit(Some(&fence), &[submit])?;
device.wait_for_fences(&[&fence], true, Duration::from_secs(1))?;
Ok(())
}
fn copy_image_to_image(
buffer_recorder: &mut CommandBufferRecorder<'_>,
src_image: &Arc<Image>,
dst_image: &Arc<Image>,
mip_level: u32,
dst_layer: u32,
) {
// copy information to get every source into the right target slot
let image_copy = VkImageCopy {
srcSubresource: VkImageSubresourceLayers {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
mipLevel: mip_level,
baseArrayLayer: 0,
layerCount: 1,
},
srcOffset: VkOffset3D { x: 0, y: 0, z: 0 },
dstSubresource: VkImageSubresourceLayers {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
mipLevel: mip_level,
baseArrayLayer: dst_layer,
layerCount: 1,
},
dstOffset: VkOffset3D { x: 0, y: 0, z: 0 },
extent: VkExtent3D {
width: src_image.width(),
height: src_image.height(),
depth: 1,
},
};
let subresource_range = VkImageSubresourceRange {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
baseMipLevel: mip_level,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
};
// set the source image into sending state
buffer_recorder.set_image_layout(
src_image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
subresource_range.clone(),
);
// copy the source data into the target slot
buffer_recorder.copy_image(
src_image,
dst_image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&[image_copy],
);
// set the source image back to a usable state for shaders
buffer_recorder.set_image_layout(
src_image,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
subresource_range,
);
}
fn blit_mip_maps(
buffer_recorder: &mut CommandBufferRecorder<'_>,
image: &Arc<Image>,
target_image_layout: VkImageLayout,
) {
let mut mip_width = image.width();
let mut mip_height = image.height();
// subresource information
let mut subresource_range = VkImageSubresourceRange {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
baseMipLevel: 0,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
};
for i in 1..image.levels() {
let source_mip_level = i - 1;
let target_mip_level = i;
// transition the previous mip level from destination to source
subresource_range.baseMipLevel = source_mip_level;
buffer_recorder.set_image_layout(
image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
subresource_range.clone(),
);
// create the blit information to blit the data from one mip level to another
let image_blit = VkImageBlit {
srcSubresource: VkImageSubresourceLayers {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
mipLevel: source_mip_level,
baseArrayLayer: 0,
layerCount: 1,
},
srcOffsets: [
VkOffset3D { x: 0, y: 0, z: 0 },
VkOffset3D {
x: mip_width as i32,
y: mip_height as i32,
z: 1,
},
],
dstSubresource: VkImageSubresourceLayers {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(),
mipLevel: target_mip_level,
baseArrayLayer: 0,
layerCount: 1,
},
dstOffsets: [
VkOffset3D { x: 0, y: 0, z: 0 },
VkOffset3D {
x: if mip_width > 1 {
mip_width as i32 / 2
} else {
1
},
y: if mip_height > 1 {
mip_height as i32 / 2
} else {
1
},
z: 1,
},
],
};
// execute the actual blit
buffer_recorder.blit_image(
image,
image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&[image_blit],
VK_FILTER_LINEAR,
);
// set mip level i - 1 to target layout
buffer_recorder.set_image_layout(image, target_image_layout, subresource_range.clone());
image.set_image_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
mip_width = if mip_width > 1 { mip_width / 2 } else { 1 };
mip_height = if mip_height > 1 { mip_height / 2 } else { 1 };
}
// set last level to be target layout
subresource_range.baseMipLevel = image.levels() - 1;
buffer_recorder.set_image_layout(image, target_image_layout, subresource_range);
}