209 lines
6.3 KiB
Rust
209 lines
6.3 KiB
Rust
use std::alloc::Layout;
|
|
|
|
pub trait GpuAllocator: Send + Sync {
|
|
/// Allocate `size` bytes with given alignment.
|
|
/// Returns a host-mapped pointer.
|
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8;
|
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
|
|
}
|
|
|
|
/// CPU fallback — standard system allocator.
|
|
pub struct SystemAllocator;
|
|
|
|
impl Default for SystemAllocator {
|
|
fn default() -> Self {
|
|
Self {}
|
|
}
|
|
}
|
|
|
|
impl GpuAllocator for SystemAllocator {
|
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
if layout.size() == 0 {
|
|
return layout.align() as *mut u8;
|
|
}
|
|
unsafe { std::alloc::alloc(layout) }
|
|
}
|
|
|
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
if layout.size() > 0 {
|
|
unsafe {
|
|
std::alloc::dealloc(ptr, layout);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// CUDA unified memory backend using CudaAllocator
|
|
#[cfg(feature = "cuda")]
|
|
pub mod cuda {
|
|
use super::GpuAllocator;
|
|
use std::alloc::Layout;
|
|
|
|
pub struct CudaAllocator;
|
|
|
|
impl Default for CudaAllocator {
|
|
fn default() -> Self {
|
|
Self {}
|
|
}
|
|
}
|
|
|
|
impl GpuAllocator for CudaAllocator {
|
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
use cust::memory::cuda_malloc_unified;
|
|
use cust_raw::driver_sys::*;
|
|
|
|
let size = layout.size().max(layout.align());
|
|
if size == 0 {
|
|
return layout.align() as *mut u8;
|
|
}
|
|
|
|
let mut ctx: CUcontext = std::ptr::null_mut();
|
|
cuCtxGetCurrent(&mut ctx);
|
|
if ctx.is_null() {
|
|
let mut primary: CUcontext = std::ptr::null_mut();
|
|
cuDevicePrimaryCtxRetain(&mut primary, 0);
|
|
cuCtxSetCurrent(primary);
|
|
}
|
|
|
|
let mut unified_ptr =
|
|
unsafe { cuda_malloc_unified::<u8>(size).expect("cuda_malloc_unified failed") };
|
|
let raw = unified_ptr.as_raw_mut();
|
|
std::mem::forget(unified_ptr);
|
|
raw
|
|
}
|
|
|
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
use cust::memory::{UnifiedPointer, cuda_free_unified};
|
|
if layout.size() > 0 {
|
|
let _ = unsafe { cuda_free_unified(UnifiedPointer::wrap(ptr)) };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Vulkan backend (gpu-allocator for now, there might be a better solution)
|
|
#[cfg(feature = "vulkan")]
|
|
pub mod vulkan {
|
|
use super::GpuAllocator;
|
|
use ash::vk;
|
|
use gpu_allocator::MemoryLocation;
|
|
use gpu_allocator::vulkan::{
|
|
Allocation, AllocationCreateDesc, AllocationScheme, Allocator, AllocatorCreateDesc,
|
|
};
|
|
use parking_lot::Mutex;
|
|
use std::alloc::Layout;
|
|
use std::collections::HashMap;
|
|
use std::sync::OnceLock;
|
|
|
|
// So, having a static allocator seems like a terrible idea
|
|
// But I cant find a way to get a functioning generic Arena constructor
|
|
// That might not even be a necessity, since rust-gpu/rust-cuda might actually handle that
|
|
// differently
|
|
static VK_ALLOCATOR: OnceLock<VulkanAllocatorInner> = OnceLock::new();
|
|
|
|
struct VulkanAllocatorInner {
|
|
state: Mutex<VulkanState>,
|
|
}
|
|
|
|
struct VulkanState {
|
|
allocator: Allocator,
|
|
allocations: HashMap<usize, Allocation>,
|
|
}
|
|
|
|
pub fn init_vulkan(
|
|
instance: &ash::Instance,
|
|
device: &ash::Device,
|
|
physical_device: vk::PhysicalDevice,
|
|
) {
|
|
VK_ALLOCATOR.get_or_init(|| {
|
|
let allocator = Allocator::new(&AllocatorCreateDesc {
|
|
instance: instance.clone(),
|
|
device: device.clone(),
|
|
physical_device,
|
|
debug_settings: Default::default(),
|
|
buffer_device_address: false,
|
|
allocation_sizes: Default::default(),
|
|
})
|
|
.expect("Failed to create Vulkan allocator");
|
|
|
|
VulkanAllocatorInner {
|
|
state: Mutex::new(VulkanState {
|
|
allocator,
|
|
allocations: HashMap::new(),
|
|
}),
|
|
}
|
|
});
|
|
}
|
|
|
|
fn inner() -> &'static VulkanAllocatorInner {
|
|
VK_ALLOCATOR
|
|
.get()
|
|
.expect("Vulkan not initialized — call init_vulkan() before Arena::default()")
|
|
}
|
|
|
|
impl Default for VulkanAllocator {
|
|
fn default() -> Self {
|
|
let _ = inner();
|
|
Self
|
|
}
|
|
}
|
|
|
|
pub struct VulkanAllocator;
|
|
|
|
// impl VulkanAllocator {
|
|
// pub fn new(allocator: Allocator) -> Self {
|
|
// Self {
|
|
// allocator: Mutex::new(allocator),
|
|
// allocations: Mutex::new(HashMap::new()),
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
impl GpuAllocator for VulkanAllocator {
|
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
let size = layout.size().max(layout.align());
|
|
if size == 0 {
|
|
return layout.align() as *mut u8;
|
|
}
|
|
|
|
let inner = inner();
|
|
let mut state = inner.state.lock();
|
|
let allocation = state
|
|
.allocator
|
|
.allocate(&AllocationCreateDesc {
|
|
name: "arena",
|
|
requirements: vk::MemoryRequirements {
|
|
size: size as u64,
|
|
alignment: layout.align() as u64,
|
|
memory_type_bits: u32::MAX,
|
|
},
|
|
location: MemoryLocation::CpuToGpu,
|
|
linear: true,
|
|
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
|
|
})
|
|
.expect("Vulkan allocation failed");
|
|
|
|
let ptr = allocation
|
|
.mapped_ptr()
|
|
.expect("Vulkan allocation not host-mapped")
|
|
.as_ptr() as *mut u8;
|
|
|
|
state.allocations.insert(ptr as usize, allocation);
|
|
ptr
|
|
}
|
|
|
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
if layout.size() == 0 {
|
|
return;
|
|
}
|
|
let inner = inner();
|
|
let mut state = inner.state.lock();
|
|
if let Some(allocation) = state.allocations.remove(&(ptr as usize)) {
|
|
state
|
|
.allocator
|
|
.free(allocation)
|
|
.expect("Vulkan free failed");
|
|
}
|
|
}
|
|
}
|
|
}
|