use crate::device::VulkanContext;
use crate::resource::{backend_err, VulkanTexture};
use ash::vk;
use impeller_hal::{Batch, Error, Result};
pub fn create_descriptor_layout(device: &ash::Device) -> Result<vk::DescriptorSetLayout> {
let mut bindings = vec![
vk::DescriptorSetLayoutBinding::default()
.binding(0)
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT),
vk::DescriptorSetLayoutBinding::default()
.binding(1)
.descriptor_type(vk::DescriptorType::SAMPLER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT),
];
for extra in 1..impeller_hal::MAX_EFFECT_TEXTURES {
bindings.push(
vk::DescriptorSetLayoutBinding::default()
.binding(image_binding(extra))
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT),
);
}
let info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&bindings);
unsafe { device.create_descriptor_set_layout(&info, None) }
.map_err(|e| backend_err("create_descriptor_set_layout", e))
}
pub fn image_binding(index: usize) -> u32 {
if index == 0 {
0
} else {
index as u32 + 1
}
}
pub fn create_sampler(device: &ash::Device) -> Result<vk::Sampler> {
let info = vk::SamplerCreateInfo::default()
.mag_filter(vk::Filter::LINEAR)
.min_filter(vk::Filter::LINEAR)
.mipmap_mode(vk::SamplerMipmapMode::LINEAR)
.max_lod(vk::LOD_CLAMP_NONE)
.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 { device.create_sampler(&info, None) }.map_err(|e| backend_err("create_sampler", e))
}
pub struct Bindings {
pool: Option<vk::DescriptorPool>,
views: Vec<vk::ImageView>,
sets: Vec<(
[Option<u32>; impeller_hal::MAX_EFFECT_TEXTURES],
vk::DescriptorSet,
)>,
placeholder: vk::DescriptorSet,
}
impl Bindings {
pub fn placeholder_only(placeholder: vk::DescriptorSet) -> Self {
Self {
pool: None,
views: Vec::new(),
sets: Vec::new(),
placeholder,
}
}
pub fn set_for(
&self,
slots: [Option<u32>; impeller_hal::MAX_EFFECT_TEXTURES],
) -> vk::DescriptorSet {
if slots.iter().all(Option::is_none) {
return self.placeholder;
}
self.sets
.iter()
.find(|(held, _)| *held == slots)
.map(|(_, set)| *set)
.unwrap_or(self.placeholder)
}
pub fn destroy(self, ctx: &VulkanContext) {
let device = ctx.raw_device();
unsafe {
for view in self.views {
device.destroy_image_view(view, None);
}
if let Some(pool) = self.pool {
device.destroy_descriptor_pool(pool, None);
}
}
}
}
pub fn create_placeholder_binding(
device: &ash::Device,
image: vk::Image,
layout: vk::DescriptorSetLayout,
sampler: vk::Sampler,
) -> Result<(vk::DescriptorPool, vk::ImageView, vk::DescriptorSet)> {
let sizes = [
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::SAMPLED_IMAGE)
.descriptor_count(impeller_hal::MAX_EFFECT_TEXTURES as u32),
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::SAMPLER)
.descriptor_count(1),
];
let pool_info = vk::DescriptorPoolCreateInfo::default()
.max_sets(1)
.pool_sizes(&sizes);
let pool = unsafe { device.create_descriptor_pool(&pool_info, None) }
.map_err(|e| backend_err("create_descriptor_pool", e))?;
let view_info = vk::ImageViewCreateInfo::default()
.image(image)
.view_type(vk::ImageViewType::TYPE_2D)
.format(vk::Format::R8G8B8A8_UNORM)
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.level_count(1)
.layer_count(1),
);
let view = match unsafe { device.create_image_view(&view_info, None) } {
Ok(view) => view,
Err(e) => {
unsafe { device.destroy_descriptor_pool(pool, None) };
return Err(backend_err("create_image_view", e));
}
};
let layouts = [layout];
let alloc = vk::DescriptorSetAllocateInfo::default()
.descriptor_pool(pool)
.set_layouts(&layouts);
let set = match unsafe { device.allocate_descriptor_sets(&alloc) } {
Ok(sets) => sets[0],
Err(e) => {
unsafe {
device.destroy_image_view(view, None);
device.destroy_descriptor_pool(pool, None);
}
return Err(backend_err("allocate_descriptor_sets", e));
}
};
let image_info = [vk::DescriptorImageInfo::default()
.image_view(view)
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)];
let sampler_info = [vk::DescriptorImageInfo::default().sampler(sampler)];
let mut writes: Vec<vk::WriteDescriptorSet> = (0..impeller_hal::MAX_EFFECT_TEXTURES)
.map(|index| {
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(image_binding(index))
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.image_info(&image_info)
})
.collect();
writes.push(
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(1)
.descriptor_type(vk::DescriptorType::SAMPLER)
.image_info(&sampler_info),
);
unsafe { device.update_descriptor_sets(&writes, &[]) };
Ok((pool, view, set))
}
pub fn build(
ctx: &mut VulkanContext,
batch: &Batch,
textures: &[&VulkanTexture],
) -> Result<Bindings> {
for slot in batch.texture_slots() {
if slot as usize >= textures.len() {
return Err(Error::Backend {
backend: "vulkan",
detail: format!(
"a draw samples texture slot {slot}, but only {} were supplied",
textures.len()
),
});
}
}
let placeholder = ctx.placeholder_set()?;
if textures.is_empty() {
return Ok(Bindings::placeholder_only(placeholder));
}
let layout = ctx.descriptor_layout()?;
let sampler = ctx.sampler()?;
let device = ctx.raw_device().clone();
let mut tuples: Vec<[Option<u32>; impeller_hal::MAX_EFFECT_TEXTURES]> = Vec::new();
for draw in batch.draws() {
let slots = draw.material.texture_slots();
if slots.iter().all(Option::is_none) {
continue;
}
if !tuples.contains(&slots) {
tuples.push(slots);
}
}
if tuples.is_empty() {
return Ok(Bindings::placeholder_only(placeholder));
}
let count = tuples.len() as u32;
let sizes = [
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::SAMPLED_IMAGE)
.descriptor_count(count * impeller_hal::MAX_EFFECT_TEXTURES as u32),
vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::SAMPLER)
.descriptor_count(count),
];
let pool_info = vk::DescriptorPoolCreateInfo::default()
.max_sets(count)
.pool_sizes(&sizes);
let pool = unsafe { device.create_descriptor_pool(&pool_info, None) }
.map_err(|e| backend_err("create_descriptor_pool", e))?;
let mut bindings = Bindings {
pool: Some(pool),
views: Vec::new(),
sets: Vec::new(),
placeholder,
};
let layouts = vec![layout; count as usize];
let alloc = vk::DescriptorSetAllocateInfo::default()
.descriptor_pool(pool)
.set_layouts(&layouts);
let raw_sets = match unsafe { device.allocate_descriptor_sets(&alloc) } {
Ok(sets) => sets,
Err(e) => {
bindings.destroy(ctx);
return Err(backend_err("allocate_descriptor_sets", e));
}
};
let mut views: Vec<vk::ImageView> = Vec::with_capacity(textures.len());
for texture in textures {
let view_info = vk::ImageViewCreateInfo::default()
.image(texture.raw_image())
.view_type(vk::ImageViewType::TYPE_2D)
.format(crate::resource::vk_format(texture.format()))
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.level_count(vk::REMAINING_MIP_LEVELS)
.layer_count(1),
);
match unsafe { device.create_image_view(&view_info, None) } {
Ok(view) => views.push(view),
Err(e) => {
bindings.views = views;
bindings.destroy(ctx);
return Err(backend_err("create_image_view", e));
}
}
}
bindings.views = views;
let blank = ctx.placeholder_view()?;
for (index, tuple) in tuples.iter().enumerate() {
let set = raw_sets[index];
let mut image_infos = Vec::with_capacity(impeller_hal::MAX_EFFECT_TEXTURES);
for slot in tuple.iter() {
let view = match slot {
Some(slot) => bindings.views[*slot as usize],
None => blank,
};
image_infos.push([vk::DescriptorImageInfo::default()
.image_view(view)
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)]);
}
let sampler_info = [vk::DescriptorImageInfo::default().sampler(sampler)];
let mut writes: Vec<vk::WriteDescriptorSet> = image_infos
.iter()
.enumerate()
.map(|(position, info)| {
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(image_binding(position))
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.image_info(info)
})
.collect();
writes.push(
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(1)
.descriptor_type(vk::DescriptorType::SAMPLER)
.image_info(&sampler_info),
);
unsafe { device.update_descriptor_sets(&writes, &[]) };
bindings.sets.push((*tuple, set));
}
Ok(bindings)
}
pub fn transition_for_sampling(
device: &ash::Device,
cmd: vk::CommandBuffer,
textures: &[&VulkanTexture],
) {
for texture in textures {
crate::resource::transition(
device,
cmd,
texture.raw_image(),
texture.layout(),
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
);
texture.set_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
}
}