Generalizing GPU framework to use Vulkan or CUDA with spirv
This commit is contained in:
parent
0b04d54346
commit
8a92d7642d
1 changed files with 147 additions and 0 deletions
147
src/utils/backend.rs
Normal file
147
src/utils/backend.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
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 struct CudaAllocator;
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
impl Default for CudaAllocator {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
impl GpuAllocator for CudaAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
use cust::memory::cuda_malloc_unified;
|
||||
|
||||
let size = layout.size().max(layout.align());
|
||||
if size == 0 {
|
||||
return layout.align() as *mut u8;
|
||||
}
|
||||
|
||||
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 using gpu-allocator.
|
||||
#[cfg(feature = "vulkan")]
|
||||
pub mod vulkan {
|
||||
use super::GpuAllocator;
|
||||
use ash::vk;
|
||||
use gpu_allocator::MemoryLocation;
|
||||
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator};
|
||||
use parking_lot::Mutex;
|
||||
use std::alloc::Layout;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct VulkanAllocator {
|
||||
allocator: Mutex<Allocator>,
|
||||
/// Track pointer -> Allocation so we can free later.
|
||||
allocations: Mutex<HashMap<usize, Allocation>>,
|
||||
}
|
||||
|
||||
impl VulkanAllocator {
|
||||
pub fn new(allocator: Allocator) -> Self {
|
||||
Self {
|
||||
allocator: Mutex::new(allocator),
|
||||
allocations: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VulkanAllocator {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
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 mut alloc = self.allocator.lock();
|
||||
let allocation = alloc
|
||||
.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;
|
||||
|
||||
self.allocations.lock().insert(ptr as usize, allocation);
|
||||
ptr
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
if layout.size() == 0 {
|
||||
return;
|
||||
}
|
||||
if let Some(allocation) = self.allocations.lock().remove(&(ptr as usize)) {
|
||||
self.allocator
|
||||
.lock()
|
||||
.free(allocation)
|
||||
.expect("Vulkan free failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue