333 lines
11 KiB
Rust
333 lines
11 KiB
Rust
//! This crate provides a fully written in Rust memory allocator for Vulkan, DirectX 12 and Metal.
|
|
//!
|
|
//! # [Windows-rs] and [winapi]
|
|
//!
|
|
//! `gpu-allocator` recently migrated from [winapi] to [windows-rs] but still provides convenient helpers to convert to and from [winapi] types, enabled when compiling with the `public-winapi` crate feature.
|
|
//!
|
|
//! [Windows-rs]: https://github.com/microsoft/windows-rs
|
|
//! [winapi]: https://github.com/retep998/winapi-rs
|
|
//!
|
|
//! # Setting up the Vulkan memory allocator
|
|
//!
|
|
//! ```no_run
|
|
//! # #[cfg(feature = "vulkan")]
|
|
//! # fn main() {
|
|
//! use gpu_allocator::vulkan::*;
|
|
//! # use ash::vk;
|
|
//! # let device = todo!();
|
|
//! # let instance = todo!();
|
|
//! # let physical_device = todo!();
|
|
//!
|
|
//! let mut allocator = Allocator::new(&AllocatorCreateDesc {
|
|
//! instance,
|
|
//! device,
|
|
//! physical_device,
|
|
//! debug_settings: Default::default(),
|
|
//! buffer_device_address: true, // Ideally, check the BufferDeviceAddressFeatures struct.
|
|
//! allocation_sizes: Default::default(),
|
|
//! });
|
|
//! # }
|
|
//! # #[cfg(not(feature = "vulkan"))]
|
|
//! # fn main() {}
|
|
//! ```
|
|
//!
|
|
//! # Simple Vulkan allocation example
|
|
//!
|
|
//! ```no_run
|
|
//! # #[cfg(feature = "vulkan")]
|
|
//! # fn main() {
|
|
//! use gpu_allocator::vulkan::*;
|
|
//! use gpu_allocator::MemoryLocation;
|
|
//! # use ash::vk;
|
|
//! # let device = todo!();
|
|
//! # let instance = todo!();
|
|
//! # let physical_device = todo!();
|
|
//! # let mut allocator = Allocator::new(&AllocatorCreateDesc {
|
|
//! # instance,
|
|
//! # device,
|
|
//! # physical_device,
|
|
//! # debug_settings: Default::default(),
|
|
//! # buffer_device_address: true, // Ideally, check the BufferDeviceAddressFeatures struct.
|
|
//! # allocation_sizes: Default::default(),
|
|
//! # }).unwrap();
|
|
//!
|
|
//! // Setup vulkan info
|
|
//! let vk_info = vk::BufferCreateInfo::default()
|
|
//! .size(512)
|
|
//! .usage(vk::BufferUsageFlags::STORAGE_BUFFER);
|
|
//!
|
|
//! let buffer = unsafe { device.create_buffer(&vk_info, None) }.unwrap();
|
|
//! let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
|
|
//!
|
|
//! let allocation = allocator
|
|
//! .allocate(&AllocationCreateDesc {
|
|
//! name: "Example allocation",
|
|
//! requirements,
|
|
//! location: MemoryLocation::CpuToGpu,
|
|
//! linear: true, // Buffers are always linear
|
|
//! allocation_scheme: AllocationScheme::GpuAllocatorManaged,
|
|
//! }).unwrap();
|
|
//!
|
|
//! // Bind memory to the buffer
|
|
//! unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()).unwrap() };
|
|
//!
|
|
//! // Cleanup
|
|
//! allocator.free(allocation).unwrap();
|
|
//! unsafe { device.destroy_buffer(buffer, None) };
|
|
//! # }
|
|
//! # #[cfg(not(feature = "vulkan"))]
|
|
//! # fn main() {}
|
|
//! ```
|
|
//!
|
|
//! # Setting up the D3D12 memory allocator
|
|
//!
|
|
//! ```no_run
|
|
//! # #[cfg(feature = "d3d12")]
|
|
//! # fn main() {
|
|
//! use gpu_allocator::d3d12::*;
|
|
//! # let device = todo!();
|
|
//!
|
|
//! let mut allocator = Allocator::new(&AllocatorCreateDesc {
|
|
//! device: ID3D12DeviceVersion::Device(device),
|
|
//! debug_settings: Default::default(),
|
|
//! allocation_sizes: Default::default(),
|
|
//! });
|
|
//! # }
|
|
//! # #[cfg(not(feature = "d3d12"))]
|
|
//! # fn main() {}
|
|
//! ```
|
|
//!
|
|
//! # Simple d3d12 allocation example
|
|
//!
|
|
//! ```no_run
|
|
//! # #[cfg(feature = "d3d12")]
|
|
//! # fn main() -> windows::core::Result<()> {
|
|
//! use gpu_allocator::d3d12::*;
|
|
//! use gpu_allocator::MemoryLocation;
|
|
//! # use windows::Win32::Graphics::{Dxgi, Direct3D12};
|
|
//! # let device = todo!();
|
|
//!
|
|
//! # let mut allocator = Allocator::new(&AllocatorCreateDesc {
|
|
//! # device: ID3D12DeviceVersion::Device(device),
|
|
//! # debug_settings: Default::default(),
|
|
//! # allocation_sizes: Default::default(),
|
|
//! # }).unwrap();
|
|
//!
|
|
//! let buffer_desc = Direct3D12::D3D12_RESOURCE_DESC {
|
|
//! Dimension: Direct3D12::D3D12_RESOURCE_DIMENSION_BUFFER,
|
|
//! Alignment: 0,
|
|
//! Width: 512,
|
|
//! Height: 1,
|
|
//! DepthOrArraySize: 1,
|
|
//! MipLevels: 1,
|
|
//! Format: Dxgi::Common::DXGI_FORMAT_UNKNOWN,
|
|
//! SampleDesc: Dxgi::Common::DXGI_SAMPLE_DESC {
|
|
//! Count: 1,
|
|
//! Quality: 0,
|
|
//! },
|
|
//! Layout: Direct3D12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
|
|
//! Flags: Direct3D12::D3D12_RESOURCE_FLAG_NONE,
|
|
//! };
|
|
//! let allocation_desc = AllocationCreateDesc::from_d3d12_resource_desc(
|
|
//! &allocator.device(),
|
|
//! &buffer_desc,
|
|
//! "Example allocation",
|
|
//! MemoryLocation::GpuOnly,
|
|
//! );
|
|
//! let allocation = allocator.allocate(&allocation_desc).unwrap();
|
|
//! let mut resource: Option<Direct3D12::ID3D12Resource> = None;
|
|
//! let hr = unsafe {
|
|
//! device.CreatePlacedResource(
|
|
//! allocation.heap(),
|
|
//! allocation.offset(),
|
|
//! &buffer_desc,
|
|
//! Direct3D12::D3D12_RESOURCE_STATE_COMMON,
|
|
//! None,
|
|
//! &mut resource,
|
|
//! )
|
|
//! }?;
|
|
//!
|
|
//! // Cleanup
|
|
//! drop(resource);
|
|
//! allocator.free(allocation).unwrap();
|
|
//! # Ok(())
|
|
//! # }
|
|
//! # #[cfg(not(feature = "d3d12"))]
|
|
//! # fn main() {}
|
|
//! ```
|
|
//!
|
|
//! # Setting up the Metal memory allocator
|
|
//!
|
|
//! ```no_run
|
|
//! # #[cfg(feature = "metal")]
|
|
//! # fn main() {
|
|
//! # use std::sync::Arc;
|
|
//! use gpu_allocator::metal::*;
|
|
//!
|
|
//! # let device = Arc::new(metal::Device::system_default().unwrap());
|
|
//! let mut allocator = Allocator::new(&AllocatorCreateDesc {
|
|
//! device: device.clone(),
|
|
//! debug_settings: Default::default(),
|
|
//! allocation_sizes: Default::default(),
|
|
//! });
|
|
//! # }
|
|
//! # #[cfg(not(feature = "metal"))]
|
|
//! # fn main() {}
|
|
//! ```
|
|
//!
|
|
//! # Simple Metal allocation example
|
|
//! ```no_run
|
|
//! # #[cfg(feature = "metal")]
|
|
//! # fn main() {
|
|
//! # use std::sync::Arc;
|
|
//! use gpu_allocator::metal::*;
|
|
//! use gpu_allocator::MemoryLocation;
|
|
//! # let device = Arc::new(metal::Device::system_default().unwrap());
|
|
//! # let mut allocator = Allocator::new(&AllocatorCreateDesc {
|
|
//! # device: device.clone(),
|
|
//! # debug_settings: Default::default(),
|
|
//! # allocation_sizes: Default::default(),
|
|
//! # })
|
|
//! # .unwrap();
|
|
//!
|
|
//! let allocation_desc = AllocationCreateDesc::buffer(
|
|
//! &device,
|
|
//! "Example allocation",
|
|
//! 512, // size in bytes
|
|
//! gpu_allocator::MemoryLocation::GpuOnly,
|
|
//! );
|
|
//! let allocation = allocator.allocate(&allocation_desc).unwrap();
|
|
//! let resource = allocation.make_buffer().unwrap();
|
|
//!
|
|
//! // Cleanup
|
|
//! drop(resource);
|
|
//! allocator.free(&allocation).unwrap();
|
|
//! # }
|
|
//! # #[cfg(not(feature = "metal"))]
|
|
//! # fn main() {}
|
|
//! ```
|
|
|
|
mod result;
|
|
pub use result::*;
|
|
|
|
pub(crate) mod allocator;
|
|
|
|
pub use allocator::{AllocationReport, AllocatorReport, MemoryBlockReport};
|
|
|
|
#[cfg(feature = "visualizer")]
|
|
pub mod visualizer;
|
|
|
|
#[cfg(feature = "vulkan")]
|
|
pub mod vulkan;
|
|
|
|
#[cfg(all(windows, feature = "d3d12"))]
|
|
pub mod d3d12;
|
|
|
|
#[cfg(all(any(target_os = "macos", target_os = "ios"), feature = "metal"))]
|
|
pub mod metal;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum MemoryLocation {
|
|
/// The allocated resource is stored at an unknown memory location; let the driver decide what's the best location
|
|
Unknown,
|
|
/// Store the allocation in GPU only accessible memory - typically this is the faster GPU resource and this should be
|
|
/// where most of the allocations live.
|
|
GpuOnly,
|
|
/// Memory useful for uploading data to the GPU and potentially for constant buffers
|
|
CpuToGpu,
|
|
/// Memory useful for CPU readback of data
|
|
GpuToCpu,
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct AllocatorDebugSettings {
|
|
/// Logs out debugging information about the various heaps the current device has on startup
|
|
pub log_memory_information: bool,
|
|
/// Logs out all memory leaks on shutdown with log level Warn
|
|
pub log_leaks_on_shutdown: bool,
|
|
/// Stores a copy of the full backtrace for every allocation made, this makes it easier to debug leaks
|
|
/// or other memory allocations, but storing stack traces has a RAM overhead so should be disabled
|
|
/// in shipping applications.
|
|
pub store_stack_traces: bool,
|
|
/// Log out every allocation as it's being made with log level Debug, rather spammy so off by default
|
|
pub log_allocations: bool,
|
|
/// Log out every free that is being called with log level Debug, rather spammy so off by default
|
|
pub log_frees: bool,
|
|
/// Log out stack traces when either `log_allocations` or `log_frees` is enabled.
|
|
pub log_stack_traces: bool,
|
|
}
|
|
|
|
impl Default for AllocatorDebugSettings {
|
|
fn default() -> Self {
|
|
Self {
|
|
log_memory_information: false,
|
|
log_leaks_on_shutdown: true,
|
|
store_stack_traces: false,
|
|
log_allocations: false,
|
|
log_frees: false,
|
|
log_stack_traces: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The sizes of the memory blocks that the allocator will create.
|
|
///
|
|
/// Useful for tuning the allocator to your application's needs. For example most games will be fine with the default
|
|
/// values, but eg. an app might want to use smaller block sizes to reduce the amount of memory used.
|
|
///
|
|
/// Clamped between 4MB and 256MB, and rounds up to the nearest multiple of 4MB for alignment reasons.
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct AllocationSizes {
|
|
/// The size of the memory blocks that will be created for the GPU only memory type.
|
|
///
|
|
/// Defaults to 256MB.
|
|
device_memblock_size: u64,
|
|
/// The size of the memory blocks that will be created for the CPU visible memory types.
|
|
///
|
|
/// Defaults to 64MB.
|
|
host_memblock_size: u64,
|
|
}
|
|
|
|
impl AllocationSizes {
|
|
pub fn new(device_memblock_size: u64, host_memblock_size: u64) -> Self {
|
|
const FOUR_MB: u64 = 4 * 1024 * 1024;
|
|
const TWO_HUNDRED_AND_FIFTY_SIX_MB: u64 = 256 * 1024 * 1024;
|
|
|
|
let mut device_memblock_size =
|
|
device_memblock_size.clamp(FOUR_MB, TWO_HUNDRED_AND_FIFTY_SIX_MB);
|
|
let mut host_memblock_size =
|
|
host_memblock_size.clamp(FOUR_MB, TWO_HUNDRED_AND_FIFTY_SIX_MB);
|
|
|
|
if device_memblock_size % FOUR_MB != 0 {
|
|
let val = device_memblock_size / FOUR_MB + 1;
|
|
device_memblock_size = val * FOUR_MB;
|
|
log::warn!(
|
|
"Device memory block size must be a multiple of 4MB, clamping to {}MB",
|
|
device_memblock_size / 1024 / 1024
|
|
)
|
|
}
|
|
|
|
if host_memblock_size % FOUR_MB != 0 {
|
|
let val = host_memblock_size / FOUR_MB + 1;
|
|
host_memblock_size = val * FOUR_MB;
|
|
log::warn!(
|
|
"Host memory block size must be a multiple of 4MB, clamping to {}MB",
|
|
host_memblock_size / 1024 / 1024
|
|
)
|
|
}
|
|
|
|
Self {
|
|
device_memblock_size,
|
|
host_memblock_size,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for AllocationSizes {
|
|
fn default() -> Self {
|
|
Self {
|
|
device_memblock_size: 256 * 1024 * 1024,
|
|
host_memblock_size: 64 * 1024 * 1024,
|
|
}
|
|
}
|
|
}
|