use super::*;
use crate::alignment::pad_raw_slice;
use std::mem::ManuallyDrop;
impl VkContext {
pub fn new_vertex_buffer(
&mut self,
data: &[VertexBufferElement],
storage_type: BufferStorageType,
_ext: Option<NewVertexBufferExt>,
) -> GResult<VertexBufferId> {
let (buf, staging) =
self.new_generic_buffer(data, storage_type, vk::BufferUsageFlags::VERTEX_BUFFER)?;
let vbo = VkVertexBuffer {
buffer: buf,
staging,
};
self.vbos.push(vbo);
Ok(VertexBufferId::from_id(self.vbos.len() - 1))
}
pub fn new_index_buffer(
&mut self,
data: &[IndexBufferElement],
storage_type: BufferStorageType,
_ext: Option<NewIndexBufferExt>,
) -> GResult<IndexBufferId> {
let (buf, staging) =
self.new_generic_buffer(data, storage_type, vk::BufferUsageFlags::INDEX_BUFFER)?;
let ibo = VkIndexBuffer {
buffer: buf,
staging,
};
self.ibos.push(ibo);
Ok(IndexBufferId::from_id(self.ibos.len() - 1))
}
pub fn new_uniform_buffer<T: Copy>(
&mut self,
data: &T,
_ext: Option<NewUniformBufferExt>,
) -> GResult<UniformBufferId> {
let (buf, staging) = self.new_generic_buffer(
std::slice::from_ref(data),
BufferStorageType::Dynamic,
vk::BufferUsageFlags::UNIFORM_BUFFER,
)?;
let ubo = VkUniformBuffer {
buffer: buf,
staging,
};
self.ubos.push(ubo);
Ok(UniformBufferId::from_id(self.ubos.len() - 1))
}
pub fn new_dynamic_uniform_buffer<T: Copy>(
&mut self,
data: &[T],
_ext: Option<NewDynamicUniformBufferExt>,
) -> GResult<DynamicUniformBufferId> {
let min_ubo_alignment = self
.core
.physical_dev_properties
.limits
.min_uniform_buffer_offset_alignment as usize;
let each_size = std::mem::size_of::<T>();
let byte_slice = unsafe {
std::slice::from_raw_parts(data.as_ptr() as *const u8, std::mem::size_of_val(data))
};
let padded_buf =
unsafe { pad_raw_slice(byte_slice, min_ubo_alignment, each_size, data.len()) };
let (buf, staging) = self.new_generic_buffer(
&padded_buf,
BufferStorageType::Dynamic,
vk::BufferUsageFlags::UNIFORM_BUFFER,
)?;
let ubo = VkDynamicUniformBuffer {
buffer: buf,
staging,
per_index_offset: padded_buf.len() / data.len(),
item_size: each_size,
};
self.dyn_ubos.push(ubo);
Ok(DynamicUniformBufferId::from_id(self.dyn_ubos.len() - 1))
}
}
pub struct VkVertexBuffer {
pub buffer: VkBuffer,
staging: Option<VkBuffer>,
}
pub struct VkIndexBuffer {
pub buffer: VkBuffer,
staging: Option<VkBuffer>,
}
pub struct VkUniformBuffer {
pub buffer: VkBuffer,
staging: Option<VkBuffer>,
}
pub struct VkDynamicUniformBuffer {
pub buffer: VkBuffer,
staging: Option<VkBuffer>,
pub per_index_offset: usize,
item_size: usize,
}
pub struct VkShaderStorageBuffer {
pub buffer: VkBuffer,
pub staging: Option<VkBuffer>,
}
impl VkVertexBuffer {
pub fn cmd_transfer(
&mut self,
dev: &Device,
cmd_buf: vk::CommandBuffer,
data: &[VertexBufferElement],
) -> GResult<()> {
cmd_transfer_generic(
dev,
cmd_buf,
&self.buffer,
self.staging.as_mut().ok_or(gpu_api_err!(
"vulkan this vertex buffer does not support transfers"
))?,
data,
self.buffer.size,
self.buffer.size,
0,
)
}
}
impl VkIndexBuffer {
pub fn cmd_transfer(
&mut self,
dev: &Device,
cmd_buf: vk::CommandBuffer,
data: &[IndexBufferElement],
) -> GResult<()> {
cmd_transfer_generic(
dev,
cmd_buf,
&self.buffer,
self.staging.as_mut().ok_or(gpu_api_err!(
"vulkan this index buffer does not support transfers"
))?,
data,
self.buffer.size,
self.buffer.size,
0,
)
}
}
impl VkUniformBuffer {
pub fn cmd_transfer<T>(
&mut self,
dev: &Device,
cmd_buf: vk::CommandBuffer,
data: &[T],
) -> GResult<()> {
cmd_transfer_generic(
dev,
cmd_buf,
&self.buffer,
self.staging.as_mut().ok_or(gpu_api_err!(
"vulkan this uniform buffer does not support transfers"
))?,
data,
self.buffer.size,
self.buffer.size,
0,
)
}
}
impl VkDynamicUniformBuffer {
pub fn cmd_transfer<T>(
&mut self,
dev: &Device,
cmd_buf: vk::CommandBuffer,
data: &[T],
index: usize,
) -> GResult<()> {
cmd_transfer_generic(
dev,
cmd_buf,
&self.buffer,
self.staging.as_mut().ok_or(gpu_api_err!(
"vulkan this dynamic uniform buffer does not support transfers"
))?,
data,
self.per_index_offset,
self.item_size,
index * self.per_index_offset,
)
}
}
fn cmd_transfer_generic<T>(
dev: &Device,
cmd_buf: vk::CommandBuffer,
buffer: &VkBuffer,
staging: &mut VkBuffer,
data: &[T],
size: usize,
item_size: usize,
offset: usize,
) -> GResult<()> {
staging.map_copy_data(data.as_ptr() as *const u8, item_size, offset)?;
VkBuffer::cmd_upload_copy_data(staging, buffer, dev, size, offset, cmd_buf);
Ok(())
}
impl VkContext {
pub fn new_generic_buffer<T>(
&mut self,
data: &[T],
storage_type: BufferStorageType,
additional_buffer_usage: vk::BufferUsageFlags,
) -> GResult<(VkBuffer, Option<VkBuffer>)> {
let buf_size = std::mem::size_of_val(data);
let mut staging = VkBuffer::new(
&self.core.dev,
&self.drop_queue,
&mut self.alloc,
buf_size,
vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::TRANSFER_DST,
MemoryLocation::CpuToGpu,
)?;
staging.map_copy_data(data.as_ptr() as *const u8, buf_size, 0)?;
let buf = VkBuffer::new(
&self.core.dev,
&self.drop_queue,
&mut self.alloc,
buf_size,
vk::BufferUsageFlags::TRANSFER_DST | additional_buffer_usage,
MemoryLocation::GpuOnly,
)?;
{
VkBuffer::single_upload_copy_data(&staging, &buf, &self.core, staging.size, 0)?;
}
Ok((
buf,
if storage_type == BufferStorageType::Dynamic {
Some(staging)
} else {
None
},
))
}
}
pub struct VkBuffer {
pub buffer: vk::Buffer,
pub allocation: ManuallyDrop<Allocation>,
pub size: usize,
pub mapped_ptr: Option<*mut u8>,
drop_queue_ref: VkDropQueueRef,
}
impl VkBuffer {
pub fn new(
dev: &Device,
drop_queue_ref: &VkDropQueueRef,
alloc: &mut Allocator,
data_size: usize,
usage: vk::BufferUsageFlags,
mem_usage: MemoryLocation,
) -> GResult<Self> {
let buffer_create = vk::BufferCreateInfo::builder()
.size(data_size as u64)
.usage(usage)
.build();
let buffer = unsafe { dev.create_buffer(&buffer_create, None) }.unwrap();
let requirements = unsafe { dev.get_buffer_memory_requirements(buffer) };
let allocation = alloc
.allocate(&AllocationCreateDesc {
name: "Vulkan Generic Buffer",
requirements,
location: mem_usage,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged
})
.unwrap();
unsafe {
dev.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())
.map_err(|e| gpu_api_err!("vulkan buffer bind {}", e))?;
};
Ok(VkBuffer {
buffer,
allocation: ManuallyDrop::new(allocation),
size: data_size,
mapped_ptr: None,
drop_queue_ref: Arc::clone(drop_queue_ref),
})
}
pub fn map_copy_data(&mut self, ptr: *const u8, size: usize, offset: usize) -> GResult<()> {
if let Some(mapped_ptr) = self.mapped_ptr {
unsafe {
std::ptr::copy_nonoverlapping::<u8>(ptr, mapped_ptr.add(offset), size);
}
} else {
let data = self
.allocation
.mapped_ptr()
.ok_or(gpu_api_err!(
"vulkan gpu_allocator, this buffer cannot be mapped"
))?
.as_ptr();
self.mapped_ptr = Some(data as *mut u8);
self.map_copy_data(ptr, size, offset)?;
}
Ok(())
}
fn cmd_upload_copy_data(
src: &Self,
dst: &Self,
dev: &Device,
size: usize,
offset: usize,
command_buf: vk::CommandBuffer,
) {
let copy_create = vk::BufferCopy::builder()
.src_offset(offset as u64)
.dst_offset(offset as u64)
.size(size as u64)
.build();
unsafe { dev.cmd_copy_buffer(command_buf, src.buffer, dst.buffer, &[copy_create]) };
}
pub fn single_upload_copy_data(
src: &Self,
dst: &Self,
core: &VkCore,
size: usize,
offset: usize,
) -> GResult<()> {
if src.size > dst.size {
Err(gpu_api_err!(
"vulkan upload copy src.size ({}) > dst.size ({})",
src.size,
dst.size
))?
}
let _misc_cmd = core.misc_command()?;
Self::cmd_upload_copy_data(src, dst, &core.dev, size, offset, core.misc_command_buffer);
Ok(())
}
}
impl Drop for VkBuffer {
fn drop(&mut self) {
let allocation = unsafe { ManuallyDrop::take(&mut self.allocation) };
let buffer = self.buffer;
self.drop_queue_ref
.lock()
.unwrap()
.push(Box::new(move |dev, alloc| unsafe {
alloc.free(allocation).unwrap();
dev.destroy_buffer(buffer, None);
}));
}
}