#![allow(non_upper_case_globals)]
use crate::{owner::Owner, traits::Class};
#[rustfmt::skip]
use physx_sys::{
create_alloc_callback,
get_alloc_callback_user_data,
get_default_allocator,
get_default_error_callback,
phys_PxCreateFoundation,
PxAllocatorCallback,
PxErrorCallback,
PxErrorCode,
PxFoundation_getAllocatorCallback_mut,
PxFoundation_getErrorCallback_mut,
PxFoundation_getErrorLevel,
PxFoundation_getReportAllocationNames,
PxFoundation_release_mut,
PxFoundation_setErrorLevel_mut,
PxFoundation_setReportAllocationNames_mut,
};
use std::{
alloc::{alloc, dealloc, Layout},
ffi::c_void,
marker::PhantomData,
mem::{align_of, size_of},
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
bitflags::bitflags! {
#[repr(transparent)]
pub struct ErrorCodes: u32 {
const DebugInfo = PxErrorCode::DebugInfo as u32;
const DebugWarning = PxErrorCode::DebugWarning as u32;
const InvalidParameter = PxErrorCode::InvalidParameter as u32;
const InvalidOperation = PxErrorCode::InvalidOperation as u32;
const OutOfMemory = PxErrorCode::OutOfMemory as u32;
const InternalError = PxErrorCode::InternalError as u32;
const Abort = PxErrorCode::Abort as u32;
const PerfWarning = PxErrorCode::PerfWarning as u32;
}
}
#[repr(transparent)]
pub struct PxFoundation<Allocator: AllocatorCallback> {
obj: physx_sys::PxFoundation,
phantom_interface: PhantomData<Allocator>,
}
impl<Allocator: AllocatorCallback> Drop for PxFoundation<Allocator> {
fn drop(&mut self) {
unsafe {
if let Some(allocator) = self.get_allocator_callback() {
drop(Box::from_raw(allocator));
};
PxFoundation_release_mut(self.as_mut_ptr());
}
}
}
unsafe impl<P, Allocator: AllocatorCallback> Class<P> for PxFoundation<Allocator>
where
physx_sys::PxFoundation: Class<P>,
{
fn as_ptr(&self) -> *const P {
self.obj.as_ptr()
}
fn as_mut_ptr(&mut self) -> *mut P {
self.obj.as_mut_ptr()
}
}
unsafe impl<Allocator: AllocatorCallback + Send> Send for PxFoundation<Allocator> {}
unsafe impl<Allocator: AllocatorCallback + Sync> Sync for PxFoundation<Allocator> {}
impl<Allocator: AllocatorCallback> Foundation for PxFoundation<Allocator> {
type Allocator = Allocator;
}
pub trait Foundation: Class<physx_sys::PxFoundation> + Sized {
type Allocator: AllocatorCallback;
fn new(allocator: Self::Allocator) -> Option<Owner<Self>> {
unsafe {
Self::with_allocator_error_callback(
allocator,
get_default_error_callback() as *mut PxErrorCallback,
)
}
}
unsafe fn with_allocator_error_callback(
allocator: Self::Allocator,
error_callback: *mut PxErrorCallback,
) -> Option<Owner<Self>> {
unsafe {
Owner::from_raw(
phys_PxCreateFoundation(
crate::physics::PX_PHYSICS_VERSION,
allocator.into_px(),
error_callback,
)
.cast::<Self>(),
)
}
}
fn get_error_callback(&mut self) -> Option<&mut PxErrorCallback> {
unsafe { PxFoundation_getErrorCallback_mut(self.as_mut_ptr()).as_mut() }
}
fn set_error_level(&mut self, mask: ErrorCodes) {
unsafe { PxFoundation_setErrorLevel_mut(self.as_mut_ptr(), mask.bits()) }
}
fn get_error_level(&self) -> ErrorCodes {
unsafe {
ErrorCodes::from_bits(PxFoundation_getErrorLevel(self.as_ptr()))
.expect("got invalid bits for error flags")
}
}
fn get_allocator_callback(&mut self) -> Option<&mut Self::Allocator> {
unsafe {
(get_alloc_callback_user_data(PxFoundation_getAllocatorCallback_mut(self.as_mut_ptr()))
as *mut Self::Allocator)
.as_mut()
}
}
fn get_report_allocation_names(&self) -> bool {
unsafe { PxFoundation_getReportAllocationNames(self.as_ptr()) }
}
fn set_report_allocation_names(&mut self, value: bool) {
unsafe { PxFoundation_setReportAllocationNames_mut(self.as_mut_ptr(), value) }
}
}
#[repr(align(16))]
struct ScratchBufferBlock([u8; 16 * 1024]);
pub struct ScratchBuffer {
ptr: *mut c_void,
size: usize,
}
impl ScratchBuffer {
#[inline]
pub(crate) fn as_ptr_and_size(&mut self) -> (*mut c_void, u32) {
(self.ptr, self.size as u32)
}
pub unsafe fn new(nb_blocks: usize) -> Self {
let size = size_of::<ScratchBufferBlock>() * nb_blocks;
let align = align_of::<ScratchBufferBlock>();
let ptr = unsafe { alloc(Layout::from_size_align(size, align).unwrap()) as *mut c_void };
Self { ptr, size }
}
}
#[allow(clippy::missing_safety_doc)]
pub unsafe trait AllocatorCallback: Sized {
unsafe extern "C" fn allocate(
size: u64,
name: *const c_void,
file: *const c_void,
line: u32,
user_data: *const c_void,
) -> *mut c_void;
unsafe extern "C" fn deallocate(ptr: *const c_void, user_data: *const c_void);
unsafe fn into_px(self) -> *mut PxAllocatorCallback {
unsafe {
create_alloc_callback(
Self::allocate,
Self::deallocate,
Box::into_raw(Box::new(self)) as *mut c_void,
)
}
}
}
pub struct GlobalAllocCallback;
unsafe impl AllocatorCallback for GlobalAllocCallback {
unsafe extern "C" fn allocate(
size: u64,
_name: *const c_void,
_file: *const c_void,
_line: u32,
_user_data: *const c_void,
) -> *mut c_void {
let alloc_size = size as usize + 16;
let layout = std::alloc::Layout::from_size_align(alloc_size, 16).unwrap();
unsafe {
let allocation = alloc(layout) as *mut u64;
*allocation = alloc_size as u64;
(allocation as usize + 16) as *mut c_void
}
}
unsafe extern "C" fn deallocate(ptr: *const c_void, _user_data: *const c_void) {
let ptr = (ptr as usize - 16) as *mut u64;
unsafe {
let size = *ptr;
let layout = Layout::from_size_align(size as usize, 16).unwrap();
dealloc(ptr as *mut u8, layout)
}
}
}
pub struct TrackingAllocator {
pub allocated: AtomicUsize,
pub deallocated: AtomicUsize,
}
impl Default for TrackingAllocator {
fn default() -> Self {
Self {
allocated: AtomicUsize::new(0),
deallocated: AtomicUsize::new(0),
}
}
}
unsafe impl AllocatorCallback for TrackingAllocator {
unsafe extern "C" fn allocate(
size: u64,
_name: *const c_void,
_file: *const c_void,
_line: u32,
user_data: *const c_void,
) -> *mut c_void {
let user_data = unsafe { &*(user_data as *const Self) };
let alloc_size = size as usize + 16;
let layout = std::alloc::Layout::from_size_align(alloc_size, 16).unwrap();
user_data.allocated.fetch_add(layout.size(), SeqCst);
unsafe {
let allocation = alloc(layout) as *mut u64;
*allocation = alloc_size as u64;
(allocation as usize + 16) as *mut c_void
}
}
unsafe extern "C" fn deallocate(ptr: *const c_void, user_data: *const c_void) {
let user_data = unsafe { &*(user_data as *const Self) };
let ptr = (ptr as usize - 16) as *mut u64;
let size = unsafe { *ptr };
let layout = Layout::from_size_align(size as usize, 16).unwrap();
user_data.deallocated.fetch_add(layout.size(), SeqCst);
unsafe { dealloc(ptr as *mut u8, layout) }
}
}
pub struct DefaultAllocator;
unsafe impl AllocatorCallback for DefaultAllocator {
unsafe fn into_px(self) -> *mut PxAllocatorCallback {
unsafe { get_default_allocator() as *mut PxAllocatorCallback }
}
unsafe extern "C" fn allocate(
_size: u64,
_name: *const c_void,
_file: *const c_void,
_line: u32,
_user_data: *const c_void,
) -> *mut c_void {
unreachable!()
}
unsafe extern "C" fn deallocate(_ptr: *const c_void, _user_data: *const c_void) {
unreachable!()
}
}