use crate::device::VulkanContext;
use crate::render::{cast_bytes, StagedBuffer};
use crate::resource::backend_err;
use ash::vk;
use impeller_hal::{Batch, Result, MATERIAL_FLOATS};
pub const MATERIAL_BYTES: u64 = (MATERIAL_FLOATS * 4) as u64;
pub fn create_layout(device: &ash::Device) -> Result<vk::DescriptorSetLayout> {
let bindings = [vk::DescriptorSetLayoutBinding::default()
.binding(0)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC)
.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 struct Materials {
pool: vk::DescriptorPool,
set: vk::DescriptorSet,
stride: u64,
}
impl Materials {
pub fn set(&self) -> vk::DescriptorSet {
self.set
}
pub fn offset(&self, index: usize) -> u32 {
(index as u64 * self.stride) as u32
}
pub fn destroy(self, device: &ash::Device) {
unsafe { device.destroy_descriptor_pool(self.pool, None) };
}
}
pub fn build(
ctx: &mut VulkanContext,
batch: &Batch,
target_format: impeller_hal::PixelFormat,
) -> Result<(StagedBuffer, Materials)> {
let stride = stride_for(ctx);
let draws = batch.draw_count().max(1);
let mut bytes = vec![0u8; (draws as u64 * stride) as usize];
for (index, draw) in batch.draws().iter().enumerate() {
let packed = draw.to_uniform(target_format);
let at = index * stride as usize;
let words = cast_bytes(&packed);
bytes[at..at + words.len()].copy_from_slice(words);
}
let buffer = ctx.upload(&bytes, vk::BufferUsageFlags::UNIFORM_BUFFER)?;
let device = ctx.raw_device().clone();
let layout = ctx.material_layout()?;
let sizes = [vk::DescriptorPoolSize::default()
.ty(vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC)
.descriptor_count(1)];
let pool_info = vk::DescriptorPoolCreateInfo::default()
.max_sets(1)
.pool_sizes(&sizes);
let pool = match unsafe { device.create_descriptor_pool(&pool_info, None) } {
Ok(pool) => pool,
Err(e) => {
ctx.release(buffer);
return Err(backend_err("create_descriptor_pool", 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_descriptor_pool(pool, None) };
ctx.release(buffer);
return Err(backend_err("allocate_descriptor_sets", e));
}
};
let info = [vk::DescriptorBufferInfo::default()
.buffer(buffer.buffer)
.offset(0)
.range(MATERIAL_BYTES)];
let writes = [vk::WriteDescriptorSet::default()
.dst_set(set)
.dst_binding(0)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC)
.buffer_info(&info)];
unsafe { device.update_descriptor_sets(&writes, &[]) };
Ok((buffer, Materials { pool, set, stride }))
}
fn stride_for(ctx: &VulkanContext) -> u64 {
let alignment = ctx.uniform_alignment().max(1);
MATERIAL_BYTES.div_ceil(alignment) * alignment
}