use gfx_hal::{
memory::{
Segment,
SparseFlags,
},
prelude::Device,
Backend,
};
use super::gpu::Gpu;
pub type Usage = gfx_hal::buffer::Usage;
pub type Properties = gfx_hal::memory::Properties;
#[derive(Debug, Clone, Copy)]
pub enum BufferType {
Vertex,
Index,
Uniform,
Storage,
}
#[derive(Debug, Clone, Copy)]
pub struct Buffer<RenderBackend: Backend> {
buffer: RenderBackend::Buffer,
memory: RenderBackend::Memory,
stride: usize,
buffer_type: BufferType,
}
impl<RenderBackend: Backend> Buffer<RenderBackend> {
pub fn destroy(self, gpu: &Gpu<RenderBackend>) {
unsafe {
gpu.internal_logical_device().free_memory(self.memory);
gpu.internal_logical_device().destroy_buffer(self.buffer);
}
}
pub fn stride(&self) -> usize {
return self.stride;
}
}
impl<RenderBackend: Backend> Buffer<RenderBackend> {
pub(super) fn internal_buffer(&self) -> &RenderBackend::Buffer {
return &self.buffer;
}
}
pub struct BufferBuilder {
buffer_length: usize,
usage: Usage,
properties: Properties,
buffer_type: BufferType,
}
impl BufferBuilder {
pub fn new() -> Self {
return Self {
buffer_length: 0,
usage: Usage::empty(),
properties: Properties::empty(),
buffer_type: BufferType::Vertex,
};
}
pub fn with_length(&mut self, length: usize) -> &mut Self {
self.buffer_length = length;
return self;
}
pub fn with_usage(&mut self, usage: Usage) -> &mut Self {
self.usage = usage;
return self;
}
pub fn with_properties(&mut self, properties: Properties) -> &mut Self {
self.properties = properties;
return self;
}
pub fn with_buffer_type(&mut self, buffer_type: BufferType) -> &mut Self {
self.buffer_type = buffer_type;
return self;
}
pub fn build<RenderBackend: Backend, Data: Sized>(
&self,
gpu: &mut Gpu<RenderBackend>,
data: Vec<Data>,
) -> Result<Buffer<RenderBackend>, &'static str> {
use gfx_hal::{
adapter::PhysicalDevice,
MemoryTypeId,
};
let logical_device = gpu.internal_logical_device();
let physical_device = gpu.internal_physical_device();
logging::debug!(
"[DEBUG] Creating buffer of length: {}",
self.buffer_length
);
let buffer_result = unsafe {
logical_device.create_buffer(
self.buffer_length as u64,
self.usage,
SparseFlags::empty(),
)
};
if buffer_result.is_err() {
logging::error!("Failed to create buffer for allocating memory.");
return Err("Failed to create buffer for allocating memory.");
}
let mut buffer = buffer_result.unwrap();
let requirements =
unsafe { logical_device.get_buffer_requirements(&buffer) };
let memory_types = physical_device.memory_properties().memory_types;
logging::debug!("Buffer requirements: {:?}", requirements);
let memory_type = memory_types
.iter()
.enumerate()
.find(|(id, memory_type)| {
let type_supported = requirements.type_mask & (1 << id) != 0;
type_supported && memory_type.properties.contains(self.properties)
})
.map(|(id, _)| MemoryTypeId(id))
.unwrap();
logging::debug!("Allocating memory for buffer.");
let buffer_memory_allocation =
unsafe { logical_device.allocate_memory(memory_type, requirements.size) };
if buffer_memory_allocation.is_err() {
logging::error!("Failed to allocate memory for buffer.");
return Err("Failed to allocate memory for buffer.");
}
let mut buffer_memory = buffer_memory_allocation.unwrap();
let buffer_binding = unsafe {
logical_device.bind_buffer_memory(&buffer_memory, 0, &mut buffer)
};
if buffer_binding.is_err() {
unsafe { logical_device.destroy_buffer(buffer) };
logging::error!("Failed to bind buffer memory.");
return Err("Failed to bind buffer memory.");
}
let get_mapping_to_memory =
unsafe { logical_device.map_memory(&mut buffer_memory, Segment::ALL) };
if get_mapping_to_memory.is_err() {
unsafe { logical_device.destroy_buffer(buffer) };
logging::error!("Failed to map memory.");
return Err("Failed to map memory.");
}
let mapped_memory = get_mapping_to_memory.unwrap();
unsafe {
std::ptr::copy_nonoverlapping(
data.as_ptr() as *const u8,
mapped_memory,
self.buffer_length,
);
};
let memory_flush = unsafe {
logical_device
.flush_mapped_memory_ranges(std::iter::once((
&buffer_memory,
Segment::ALL,
)))
.map_err(|_| "Failed to flush memory.")
};
if memory_flush.is_err() {
unsafe { logical_device.destroy_buffer(buffer) };
logging::error!("Failed to flush memory.");
return Err("No memory available on the GPU.");
}
unsafe { logical_device.unmap_memory(&mut buffer_memory) };
return Ok(Buffer {
buffer,
memory: buffer_memory,
stride: std::mem::size_of::<Data>(),
buffer_type: self.buffer_type,
});
}
}