gpu_allocator/
lib.rs

1//! This crate provides a fully written in Rust memory allocator for Vulkan, DirectX 12 and Metal.
2//!
3//! # [Windows-rs] and [winapi]
4//!
5//! `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.
6//!
7//! [Windows-rs]: https://github.com/microsoft/windows-rs
8//! [winapi]: https://github.com/retep998/winapi-rs
9//!
10//! # Setting up the Vulkan memory allocator
11//!
12//! ```no_run
13//! # #[cfg(feature = "vulkan")]
14//! # fn main() {
15//! use gpu_allocator::vulkan::*;
16//! # use ash::vk;
17//! # let device = todo!();
18//! # let instance = todo!();
19//! # let physical_device = todo!();
20//!
21//! let mut allocator = Allocator::new(&AllocatorCreateDesc {
22//!     instance,
23//!     device,
24//!     physical_device,
25//!     debug_settings: Default::default(),
26//!     buffer_device_address: true,  // Ideally, check the BufferDeviceAddressFeatures struct.
27//!     allocation_sizes: Default::default(),
28//! });
29//! # }
30//! # #[cfg(not(feature = "vulkan"))]
31//! # fn main() {}
32//! ```
33//!
34//! # Simple Vulkan allocation example
35//!
36//! ```no_run
37//! # #[cfg(feature = "vulkan")]
38//! # fn main() {
39//! use gpu_allocator::vulkan::*;
40//! use gpu_allocator::MemoryLocation;
41//! # use ash::vk;
42//! # let device = todo!();
43//! # let instance = todo!();
44//! # let physical_device = todo!();
45//! # let mut allocator = Allocator::new(&AllocatorCreateDesc {
46//! #     instance,
47//! #     device,
48//! #     physical_device,
49//! #     debug_settings: Default::default(),
50//! #     buffer_device_address: true,  // Ideally, check the BufferDeviceAddressFeatures struct.
51//! #     allocation_sizes: Default::default(),
52//! # }).unwrap();
53//!
54//! // Setup vulkan info
55//! let vk_info = vk::BufferCreateInfo::default()
56//!     .size(512)
57//!     .usage(vk::BufferUsageFlags::STORAGE_BUFFER);
58//!
59//! let buffer = unsafe { device.create_buffer(&vk_info, None) }.unwrap();
60//! let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
61//!
62//! let allocation = allocator
63//!     .allocate(&AllocationCreateDesc {
64//!         name: "Example allocation",
65//!         requirements,
66//!         location: MemoryLocation::CpuToGpu,
67//!         linear: true, // Buffers are always linear
68//!         allocation_scheme: AllocationScheme::GpuAllocatorManaged,
69//!     }).unwrap();
70//!
71//! // Bind memory to the buffer
72//! unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()).unwrap() };
73//!
74//! // Cleanup
75//! allocator.free(allocation).unwrap();
76//! unsafe { device.destroy_buffer(buffer, None) };
77//! # }
78//! # #[cfg(not(feature = "vulkan"))]
79//! # fn main() {}
80//! ```
81//!
82//! # Setting up the D3D12 memory allocator
83//!
84//! ```no_run
85//! # #[cfg(feature = "d3d12")]
86//! # fn main() {
87//! use gpu_allocator::d3d12::*;
88//! # let device = todo!();
89//!
90//! let mut allocator = Allocator::new(&AllocatorCreateDesc {
91//!     device: ID3D12DeviceVersion::Device(device),
92//!     debug_settings: Default::default(),
93//!     allocation_sizes: Default::default(),
94//! });
95//! # }
96//! # #[cfg(not(feature = "d3d12"))]
97//! # fn main() {}
98//! ```
99//!
100//! # Simple d3d12 allocation example
101//!
102//! ```no_run
103//! # #[cfg(feature = "d3d12")]
104//! # fn main() -> windows::core::Result<()> {
105//! use gpu_allocator::d3d12::*;
106//! use gpu_allocator::MemoryLocation;
107//! # use windows::Win32::Graphics::{Dxgi, Direct3D12};
108//! # let device = todo!();
109//!
110//! # let mut allocator = Allocator::new(&AllocatorCreateDesc {
111//! #     device: ID3D12DeviceVersion::Device(device),
112//! #     debug_settings: Default::default(),
113//! #     allocation_sizes: Default::default(),
114//! # }).unwrap();
115//!
116//! let buffer_desc = Direct3D12::D3D12_RESOURCE_DESC {
117//!     Dimension: Direct3D12::D3D12_RESOURCE_DIMENSION_BUFFER,
118//!     Alignment: 0,
119//!     Width: 512,
120//!     Height: 1,
121//!     DepthOrArraySize: 1,
122//!     MipLevels: 1,
123//!     Format: Dxgi::Common::DXGI_FORMAT_UNKNOWN,
124//!     SampleDesc: Dxgi::Common::DXGI_SAMPLE_DESC {
125//!         Count: 1,
126//!         Quality: 0,
127//!     },
128//!     Layout: Direct3D12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
129//!     Flags: Direct3D12::D3D12_RESOURCE_FLAG_NONE,
130//! };
131//! let allocation_desc = AllocationCreateDesc::from_d3d12_resource_desc(
132//!     &allocator.device(),
133//!     &buffer_desc,
134//!     "Example allocation",
135//!     MemoryLocation::GpuOnly,
136//! );
137//! let allocation = allocator.allocate(&allocation_desc).unwrap();
138//! let mut resource: Option<Direct3D12::ID3D12Resource> = None;
139//! let hr = unsafe {
140//!     device.CreatePlacedResource(
141//!         allocation.heap(),
142//!         allocation.offset(),
143//!         &buffer_desc,
144//!         Direct3D12::D3D12_RESOURCE_STATE_COMMON,
145//!         None,
146//!         &mut resource,
147//!     )
148//! }?;
149//!
150//! // Cleanup
151//! drop(resource);
152//! allocator.free(allocation).unwrap();
153//! # Ok(())
154//! # }
155//! # #[cfg(not(feature = "d3d12"))]
156//! # fn main() {}
157//! ```
158//!
159//! # Setting up the Metal memory allocator
160//!
161//! ```no_run
162//! # #[cfg(feature = "metal")]
163//! # fn main() {
164//! # use std::sync::Arc;
165//! use gpu_allocator::metal::*;
166//!
167//! # let device = Arc::new(metal::Device::system_default().unwrap());
168//! let mut allocator = Allocator::new(&AllocatorCreateDesc {
169//!     device: device.clone(),
170//!     debug_settings: Default::default(),
171//!     allocation_sizes: Default::default(),
172//! });
173//! # }
174//! # #[cfg(not(feature = "metal"))]
175//! # fn main() {}
176//! ```
177//!
178//! # Simple Metal allocation example
179//! ```no_run
180//! # #[cfg(feature = "metal")]
181//! # fn main() {
182//! # use std::sync::Arc;
183//! use gpu_allocator::metal::*;
184//! use gpu_allocator::MemoryLocation;
185//! # let device = Arc::new(metal::Device::system_default().unwrap());
186//! # let mut allocator = Allocator::new(&AllocatorCreateDesc {
187//! #     device: device.clone(),
188//! #     debug_settings: Default::default(),
189//! #     allocation_sizes: Default::default(),
190//! # })
191//! # .unwrap();
192//!
193//! let allocation_desc = AllocationCreateDesc::buffer(
194//!     &device,
195//!     "Example allocation",
196//!     512, // size in bytes
197//!     gpu_allocator::MemoryLocation::GpuOnly,
198//! );
199//! let allocation = allocator.allocate(&allocation_desc).unwrap();
200//! let resource = allocation.make_buffer().unwrap();
201//!
202//! // Cleanup
203//! drop(resource);
204//! allocator.free(&allocation).unwrap();
205//! # }
206//! # #[cfg(not(feature = "metal"))]
207//! # fn main() {}
208//! ```
209
210mod result;
211pub use result::*;
212
213pub(crate) mod allocator;
214
215pub use allocator::{AllocationReport, AllocatorReport, MemoryBlockReport};
216
217#[cfg(feature = "visualizer")]
218pub mod visualizer;
219
220#[cfg(feature = "vulkan")]
221pub mod vulkan;
222
223#[cfg(all(windows, feature = "d3d12"))]
224pub mod d3d12;
225
226#[cfg(all(any(target_os = "macos", target_os = "ios"), feature = "metal"))]
227pub mod metal;
228
229#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
230pub enum MemoryLocation {
231    /// The allocated resource is stored at an unknown memory location; let the driver decide what's the best location
232    Unknown,
233    /// Store the allocation in GPU only accessible memory - typically this is the faster GPU resource and this should be
234    /// where most of the allocations live.
235    GpuOnly,
236    /// Memory useful for uploading data to the GPU and potentially for constant buffers
237    CpuToGpu,
238    /// Memory useful for CPU readback of data
239    GpuToCpu,
240}
241
242#[derive(Copy, Clone, Debug)]
243pub struct AllocatorDebugSettings {
244    /// Logs out debugging information about the various heaps the current device has on startup
245    pub log_memory_information: bool,
246    /// Logs out all memory leaks on shutdown with log level Warn
247    pub log_leaks_on_shutdown: bool,
248    /// Stores a copy of the full backtrace for every allocation made, this makes it easier to debug leaks
249    /// or other memory allocations, but storing stack traces has a RAM overhead so should be disabled
250    /// in shipping applications.
251    pub store_stack_traces: bool,
252    /// Log out every allocation as it's being made with log level Debug, rather spammy so off by default
253    pub log_allocations: bool,
254    /// Log out every free that is being called with log level Debug, rather spammy so off by default
255    pub log_frees: bool,
256    /// Log out stack traces when either `log_allocations` or `log_frees` is enabled.
257    pub log_stack_traces: bool,
258}
259
260impl Default for AllocatorDebugSettings {
261    fn default() -> Self {
262        Self {
263            log_memory_information: false,
264            log_leaks_on_shutdown: true,
265            store_stack_traces: false,
266            log_allocations: false,
267            log_frees: false,
268            log_stack_traces: false,
269        }
270    }
271}
272
273/// The sizes of the memory blocks that the allocator will create.
274///
275/// Useful for tuning the allocator to your application's needs. For example most games will be fine with the default
276/// values, but eg. an app might want to use smaller block sizes to reduce the amount of memory used.
277///
278/// Clamped between 4MB and 256MB, and rounds up to the nearest multiple of 4MB for alignment reasons.
279#[derive(Clone, Copy, Debug)]
280pub struct AllocationSizes {
281    /// The size of the memory blocks that will be created for the GPU only memory type.
282    ///
283    /// Defaults to 256MB.
284    device_memblock_size: u64,
285    /// The size of the memory blocks that will be created for the CPU visible memory types.
286    ///
287    /// Defaults to 64MB.
288    host_memblock_size: u64,
289}
290
291impl AllocationSizes {
292    pub fn new(device_memblock_size: u64, host_memblock_size: u64) -> Self {
293        const FOUR_MB: u64 = 4 * 1024 * 1024;
294        const TWO_HUNDRED_AND_FIFTY_SIX_MB: u64 = 256 * 1024 * 1024;
295
296        let mut device_memblock_size =
297            device_memblock_size.clamp(FOUR_MB, TWO_HUNDRED_AND_FIFTY_SIX_MB);
298        let mut host_memblock_size =
299            host_memblock_size.clamp(FOUR_MB, TWO_HUNDRED_AND_FIFTY_SIX_MB);
300
301        if device_memblock_size % FOUR_MB != 0 {
302            let val = device_memblock_size / FOUR_MB + 1;
303            device_memblock_size = val * FOUR_MB;
304            log::warn!(
305                "Device memory block size must be a multiple of 4MB, clamping to {}MB",
306                device_memblock_size / 1024 / 1024
307            )
308        }
309
310        if host_memblock_size % FOUR_MB != 0 {
311            let val = host_memblock_size / FOUR_MB + 1;
312            host_memblock_size = val * FOUR_MB;
313            log::warn!(
314                "Host memory block size must be a multiple of 4MB, clamping to {}MB",
315                host_memblock_size / 1024 / 1024
316            )
317        }
318
319        Self {
320            device_memblock_size,
321            host_memblock_size,
322        }
323    }
324}
325
326impl Default for AllocationSizes {
327    fn default() -> Self {
328        Self {
329            device_memblock_size: 256 * 1024 * 1024,
330            host_memblock_size: 64 * 1024 * 1024,
331        }
332    }
333}