use ash::vk;
use concinnity_core::render::error::RenderResult;
use super::allocator::{DeviceAllocator, PooledBuffer};
use super::texture;
use crate::vulkan::owned::{OwnedSetLayout, VkDevice};
mod geometry;
mod geometry_rebuild;
mod skinning;
mod streaming;
mod textures;
pub(in crate::vulkan) fn create_descriptor_set_layout(
device: &VkDevice,
bindings: &[(u32, vk::DescriptorType, vk::ShaderStageFlags)],
) -> RenderResult<OwnedSetLayout> {
let vk_bindings: Vec<_> = bindings
.iter()
.map(|&(b, ty, stage)| {
vk::DescriptorSetLayoutBinding::default()
.binding(b)
.descriptor_type(ty)
.descriptor_count(1)
.stage_flags(stage)
})
.collect();
let info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&vk_bindings);
device
.create_descriptor_set_layout(&info)
.map_err(|e| crate::vulkan::error::map_vk_result(e, "descriptor set layout"))
}
pub(in crate::vulkan) fn source_set_bindings(
n: u32,
) -> Vec<(u32, vk::DescriptorType, vk::ShaderStageFlags)> {
(0..2 * n)
.map(|b| {
let ty = if b < n {
vk::DescriptorType::SAMPLED_IMAGE
} else {
vk::DescriptorType::SAMPLER
};
(b, ty, vk::ShaderStageFlags::FRAGMENT)
})
.collect()
}
pub(in crate::vulkan) const MAX_SOURCES: usize = 8;
pub(in crate::vulkan) fn write_source_set(
device: &VkDevice,
set: vk::DescriptorSet,
sources: &[(vk::ImageView, vk::Sampler)],
) {
let n = sources.len();
assert!(
(1..=MAX_SOURCES).contains(&n),
"a source set holds 1..={MAX_SOURCES} sources, not {n}"
);
let mut images = [vk::DescriptorImageInfo::default(); MAX_SOURCES];
let mut samplers = [vk::DescriptorImageInfo::default(); MAX_SOURCES];
for (i, &(view, sampler)) in sources.iter().enumerate() {
images[i] = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(view);
samplers[i] = vk::DescriptorImageInfo::default().sampler(sampler);
}
let writes = [
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(0)
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.image_info(&images[..n]),
vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(n as u32)
.descriptor_type(vk::DescriptorType::SAMPLER)
.image_info(&samplers[..n]),
];
unsafe { device.update_descriptor_sets(&writes, &[]) };
}
pub(in crate::vulkan) fn write_samplers(
device: &VkDevice,
set: vk::DescriptorSet,
first: u32,
samplers: &[vk::Sampler],
) {
let n = samplers.len();
assert!(
(1..=MAX_SOURCES).contains(&n),
"a sampler run holds 1..={MAX_SOURCES} samplers, not {n}"
);
let mut infos = [vk::DescriptorImageInfo::default(); MAX_SOURCES];
for (info, &sampler) in infos.iter_mut().zip(samplers) {
*info = vk::DescriptorImageInfo::default().sampler(sampler);
}
let write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(first)
.descriptor_type(vk::DescriptorType::SAMPLER)
.image_info(&infos[..n]);
unsafe { device.update_descriptor_sets(std::slice::from_ref(&write), &[]) };
}
pub(in crate::vulkan) fn write_storage_buffer(
device: &VkDevice,
set: vk::DescriptorSet,
binding: u32,
buffer: vk::Buffer,
range: u64,
) {
let info = vk::DescriptorBufferInfo::default()
.buffer(buffer)
.offset(0)
.range(range);
let write = vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(binding)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(std::slice::from_ref(&info));
unsafe { device.update_descriptor_sets(std::slice::from_ref(&write), &[]) };
}
pub(in crate::vulkan) fn alloc_descriptor_sets(
device: &VkDevice,
pool: vk::DescriptorPool,
layouts: &[vk::DescriptorSetLayout],
) -> RenderResult<Vec<vk::DescriptorSet>> {
if layouts.is_empty() {
return Ok(vec![]);
}
let alloc = vk::DescriptorSetAllocateInfo::default()
.descriptor_pool(pool)
.set_layouts(layouts);
unsafe { device.allocate_descriptor_sets(&alloc) }
.map_err(|e| crate::vulkan::error::map_vk_result(e, "allocate descriptor sets"))
}
pub(in crate::vulkan) fn shared_geometry_usage(rt_capable: bool) -> vk::BufferUsageFlags {
let base = vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::TRANSFER_DST;
if rt_capable {
base | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS
| vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR
| vk::BufferUsageFlags::STORAGE_BUFFER
} else {
base
}
}
pub(in crate::vulkan) fn upload_geometry_buffer<T: bytemuck::NoUninit>(
alloc: &DeviceAllocator,
device: &VkDevice,
command_pool: vk::CommandPool,
queue: vk::Queue,
data: &[T],
usage: vk::BufferUsageFlags,
) -> RenderResult<PooledBuffer> {
upload_geometry_buffer_raw(
alloc,
device,
command_pool,
queue,
bytemuck::cast_slice(data),
usage,
)
}
pub(in crate::vulkan) fn upload_geometry_buffer_raw(
alloc: &DeviceAllocator,
device: &VkDevice,
command_pool: vk::CommandPool,
queue: vk::Queue,
data: &[u8],
usage: vk::BufferUsageFlags,
) -> RenderResult<PooledBuffer> {
let usage = usage | vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::TRANSFER_DST;
let size = data.len() as u64;
if size == 0 {
return alloc.create_buffer(4, usage, vk::MemoryPropertyFlags::DEVICE_LOCAL);
}
let staging = alloc.create_buffer(
size,
vk::BufferUsageFlags::TRANSFER_SRC,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)?;
staging.write_bytes(0, data);
let buf = alloc.create_buffer(size, usage, vk::MemoryPropertyFlags::DEVICE_LOCAL)?;
texture::one_shot_submit(device, command_pool, queue, |cmd| {
let copy = vk::BufferCopy::default().size(size);
unsafe {
device.cmd_copy_buffer(
cmd,
staging.buffer(),
buf.buffer(),
std::slice::from_ref(©),
)
};
})?;
Ok(buf)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_source_set_lists_every_image_then_every_sampler() {
use vk::DescriptorType as T;
let stage = vk::ShaderStageFlags::FRAGMENT;
assert_eq!(
source_set_bindings(3),
[
(0, T::SAMPLED_IMAGE, stage),
(1, T::SAMPLED_IMAGE, stage),
(2, T::SAMPLED_IMAGE, stage),
(3, T::SAMPLER, stage),
(4, T::SAMPLER, stage),
(5, T::SAMPLER, stage),
]
);
assert!(source_set_bindings(0).is_empty());
}
}