Skip to content

Commit e6dbe11

Browse files
authored
Add support for growing the memory block sizes (#254)
And fix the vulkan allocator ignoring the allocation_sizes parameter.
1 parent 222b3d8 commit e6dbe11

File tree

4 files changed

+116
-49
lines changed

4 files changed

+116
-49
lines changed

src/d3d12/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -440,11 +440,8 @@ impl MemoryType {
440440
) -> Result<Allocation> {
441441
let allocation_type = AllocationType::Linear;
442442

443-
let memblock_size = if self.heap_properties.Type == D3D12_HEAP_TYPE_DEFAULT {
444-
allocation_sizes.device_memblock_size
445-
} else {
446-
allocation_sizes.host_memblock_size
447-
};
443+
let is_host = self.heap_properties.Type != D3D12_HEAP_TYPE_DEFAULT;
444+
let memblock_size = allocation_sizes.get_memblock_size(is_host, self.active_general_blocks);
448445

449446
let size = desc.size;
450447
let alignment = desc.alignment;

src/lib.rs

Lines changed: 107 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -273,62 +273,138 @@ impl Default for AllocatorDebugSettings {
273273

274274
/// The sizes of the memory blocks that the allocator will create.
275275
///
276-
/// Useful for tuning the allocator to your application's needs. For example most games will be fine with the default
276+
/// Useful for tuning the allocator to your application's needs. For example most games will be fine with the defaultsize
277277
/// values, but eg. an app might want to use smaller block sizes to reduce the amount of memory used.
278278
///
279279
/// Clamped between 4MB and 256MB, and rounds up to the nearest multiple of 4MB for alignment reasons.
280+
///
281+
/// # Fixed or growable block size
282+
///
283+
/// This structure represents ranges of allowed sizes for shared memory blocks.
284+
/// By default, If the bounds of a given range are equal, the allocator will
285+
/// be configured to used a fixed memory block size for shared allocations.
286+
///
287+
/// Otherwise, the allocator will pick a memory block size within the specifed
288+
/// range, dependending on the number of existing allocations for the memory
289+
/// type.
290+
/// As a rule of thumb, the allocator will start with the minimum block size
291+
/// and double the size with each new allocation, up to the specified maximum
292+
/// block size. This growth is tracked independently for each memory type.
293+
/// The block size also decreases when blocks are deallocated.
294+
///
295+
/// # Example
296+
///
297+
/// ```
298+
/// use gpu_allocator::AllocationSizes;
299+
/// const MB: u64 = 1024 * 1024;
300+
/// // This configuration uses fixed memory block sizes.
301+
/// let fixed = AllocationSizes::new(256 * MB, 64 * MB);
302+
///
303+
/// // This configuration starts with 8MB memory blocks
304+
/// // and grows the block size of a given memory type each
305+
/// // time a new allocation is needed, up to a limit of
306+
/// // 256MB for device memory and 64MB for host memory.
307+
/// let growing = AllocationSizes::new(8 * MB, 8 * MB)
308+
/// .with_max_device_memblock_size(256 * MB)
309+
/// .with_max_host_memblock_size(64 * MB);
310+
/// ```
280311
#[derive(Clone, Copy, Debug)]
281312
pub struct AllocationSizes {
282-
/// The size of the memory blocks that will be created for the GPU only memory type.
313+
/// The initial size of the memory blocks that will be created for the GPU only memory type.
283314
///
284315
/// Defaults to 256MB.
285-
device_memblock_size: u64,
316+
min_device_memblock_size: u64,
317+
/// The size of device memory blocks doubles each time a new allocation is needed, up to
318+
/// `device_maximum_memblock_size`.
319+
max_device_memblock_size: u64,
286320
/// The size of the memory blocks that will be created for the CPU visible memory types.
287321
///
288322
/// Defaults to 64MB.
289-
host_memblock_size: u64,
323+
min_host_memblock_size: u64,
324+
/// The size of host memory blocks doubles each time a new allocation is needed, up to
325+
/// `host_maximum_memblock_size`.
326+
max_host_memblock_size: u64,
290327
}
291328

292329
impl AllocationSizes {
330+
/// Sets the minimum device and host memory block sizes.
331+
///
332+
/// The maximum block sizes are initialized to the minimum sizes and
333+
/// can be changed using `with_max_device_memblock_size` and
334+
/// `with_max_host_memblock_size`.
293335
pub fn new(device_memblock_size: u64, host_memblock_size: u64) -> Self {
294-
const FOUR_MB: u64 = 4 * 1024 * 1024;
295-
const TWO_HUNDRED_AND_FIFTY_SIX_MB: u64 = 256 * 1024 * 1024;
296-
297-
let mut device_memblock_size =
298-
device_memblock_size.clamp(FOUR_MB, TWO_HUNDRED_AND_FIFTY_SIX_MB);
299-
let mut host_memblock_size =
300-
host_memblock_size.clamp(FOUR_MB, TWO_HUNDRED_AND_FIFTY_SIX_MB);
336+
let device_memblock_size = Self::adjust_memblock_size(device_memblock_size, "Device");
337+
let host_memblock_size = Self::adjust_memblock_size(host_memblock_size, "Host");
301338

302-
if device_memblock_size % FOUR_MB != 0 {
303-
let val = device_memblock_size / FOUR_MB + 1;
304-
device_memblock_size = val * FOUR_MB;
305-
log::warn!(
306-
"Device memory block size must be a multiple of 4MB, clamping to {}MB",
307-
device_memblock_size / 1024 / 1024
308-
)
339+
Self {
340+
min_device_memblock_size: device_memblock_size,
341+
max_device_memblock_size: device_memblock_size,
342+
min_host_memblock_size: host_memblock_size,
343+
max_host_memblock_size: host_memblock_size,
309344
}
345+
}
310346

311-
if host_memblock_size % FOUR_MB != 0 {
312-
let val = host_memblock_size / FOUR_MB + 1;
313-
host_memblock_size = val * FOUR_MB;
314-
log::warn!(
315-
"Host memory block size must be a multiple of 4MB, clamping to {}MB",
316-
host_memblock_size / 1024 / 1024
317-
)
318-
}
347+
/// Sets the maximum device memblock size, in bytes.
348+
pub fn with_max_device_memblock_size(mut self, size: u64) -> Self {
349+
self.max_device_memblock_size =
350+
Self::adjust_memblock_size(size, "Device").max(self.min_device_memblock_size);
319351

320-
Self {
321-
device_memblock_size,
322-
host_memblock_size,
352+
self
353+
}
354+
355+
/// Sets the maximum host memblock size, in bytes.
356+
pub fn with_max_host_memblock_size(mut self, size: u64) -> Self {
357+
self.max_host_memblock_size =
358+
Self::adjust_memblock_size(size, "Host").max(self.min_host_memblock_size);
359+
360+
self
361+
}
362+
363+
fn adjust_memblock_size(size: u64, kind: &str) -> u64 {
364+
const MB: u64 = 1024 * 1024;
365+
366+
let size = size.clamp(4 * MB, 256 * MB);
367+
368+
if size % (4 * MB) == 0 {
369+
return size;
323370
}
371+
372+
let val = size / (4 * MB) + 1;
373+
let new_size = val * 4 * MB;
374+
log::warn!(
375+
"{kind} memory block size must be a multiple of 4MB, clamping to {}MB",
376+
new_size / MB
377+
);
378+
379+
new_size
380+
}
381+
382+
/// Used internally to decide the size of a shared memory block
383+
/// based withing the allowed range, based on the number of
384+
/// existing allocations
385+
pub(crate) fn get_memblock_size(&self, is_host: bool, count: usize) -> u64 {
386+
let (min_size, max_size) = if is_host {
387+
(self.min_host_memblock_size, self.max_host_memblock_size)
388+
} else {
389+
(self.min_device_memblock_size, self.max_device_memblock_size)
390+
};
391+
392+
// The ranges are clamped to 4MB..256MB so we never need to
393+
// shift by more than 7 bits. Clamping here to avoid having
394+
// to worry about overflows.
395+
let shift = count.min(7) as u64;
396+
(min_size << shift).min(max_size)
324397
}
325398
}
326399

327400
impl Default for AllocationSizes {
328401
fn default() -> Self {
402+
const MB: u64 = 1024 * 1024;
329403
Self {
330-
device_memblock_size: 256 * 1024 * 1024,
331-
host_memblock_size: 64 * 1024 * 1024,
404+
min_device_memblock_size: 256 * MB,
405+
max_device_memblock_size: 256 * MB,
406+
min_host_memblock_size: 64 * MB,
407+
max_host_memblock_size: 64 * MB,
332408
}
333409
}
334410
}

src/metal/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,8 @@ impl MemoryType {
248248
) -> Result<Allocation> {
249249
let allocation_type = allocator::AllocationType::Linear;
250250

251-
let memblock_size = if self.heap_properties.storageMode() == MTLStorageMode::Private {
252-
allocation_sizes.device_memblock_size
253-
} else {
254-
allocation_sizes.host_memblock_size
255-
};
251+
let is_host = self.heap_properties.storageMode() != MTLStorageMode::Private;
252+
let memblock_size = allocation_sizes.get_memblock_size(is_host, self.active_general_blocks);
256253

257254
let size = desc.size;
258255
let alignment = desc.alignment;

src/vulkan/mod.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -461,14 +461,11 @@ impl MemoryType {
461461
allocator::AllocationType::NonLinear
462462
};
463463

464-
let memblock_size = if self
464+
let is_host = self
465465
.memory_properties
466-
.contains(vk::MemoryPropertyFlags::HOST_VISIBLE)
467-
{
468-
allocation_sizes.host_memblock_size
469-
} else {
470-
allocation_sizes.device_memblock_size
471-
};
466+
.contains(vk::MemoryPropertyFlags::HOST_VISIBLE);
467+
468+
let memblock_size = allocation_sizes.get_memblock_size(is_host, self.active_general_blocks);
472469

473470
let size = desc.requirements.size;
474471
let alignment = desc.requirements.alignment;
@@ -760,7 +757,7 @@ impl Allocator {
760757
device: desc.device.clone(),
761758
buffer_image_granularity: granularity,
762759
debug_settings: desc.debug_settings,
763-
allocation_sizes: AllocationSizes::default(),
760+
allocation_sizes: desc.allocation_sizes,
764761
})
765762
}
766763

0 commit comments

Comments
 (0)