From 23d97047885100859650f66c5e34e75660157eaf Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Fri, 12 May 2023 05:36:00 -0400 Subject: [PATCH] Configurable allocation sizes (#161) This adds an AllocationSizes struct to both the Vulkan and DX12 Allocator and AllocatorCreateDesc which allows users to configure the size of the memory blocks that are created to be suballocated from. This doesn't currently clamp it to 256MB, which afaik is a problem for systems that don't support Resizable BAR, but I also don't know how to properly detect if someone is or isn't using Resizable BAR. I also don't know if we need to enforce alignment, and if so what values do we need to use for alignment? Commits: * add AllocationSizes struct * memblock_size 4MB alignment and clamp between 4MB and 256MB * readme --- README.md | 2 + examples/d3d12-buffer-winrs.rs | 1 + examples/d3d12-buffer.rs | 1 + examples/d3d12-visualization/main.rs | 1 + examples/vulkan-buffer.rs | 1 + examples/vulkan-visualization/main.rs | 1 + src/d3d12/mod.rs | 15 ++++--- src/lib.rs | 59 +++++++++++++++++++++++++++ src/vulkan/mod.rs | 16 +++++--- 9 files changed, 85 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d6faf411..e3fbffce 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ let mut allocator = Allocator::new(&AllocatorCreateDesc { physical_device, debug_settings: Default::default(), buffer_device_address: true, // Ideally, check the BufferDeviceAddressFeatures struct. + allocation_sizes: Default::default(), }); ``` @@ -78,6 +79,7 @@ use gpu_allocator::d3d12::*; let mut allocator = Allocator::new(&AllocatorCreateDesc { device, debug_settings: Default::default(), + allocation_sizes: Default::default(), }); ``` diff --git a/examples/d3d12-buffer-winrs.rs b/examples/d3d12-buffer-winrs.rs index 423b38ab..deb7d94d 100644 --- a/examples/d3d12-buffer-winrs.rs +++ b/examples/d3d12-buffer-winrs.rs @@ -90,6 +90,7 @@ fn main() -> Result<()> { let mut allocator = Allocator::new(&AllocatorCreateDesc { device: device.clone(), debug_settings: Default::default(), + allocation_sizes: Default::default(), }) .unwrap(); diff --git a/examples/d3d12-buffer.rs b/examples/d3d12-buffer.rs index 26bb9b67..bd9ff2df 100644 --- a/examples/d3d12-buffer.rs +++ b/examples/d3d12-buffer.rs @@ -119,6 +119,7 @@ fn main() { let mut allocator = Allocator::new(&AllocatorCreateDesc { device: device.as_windows().clone(), debug_settings: Default::default(), + allocation_sizes: Default::default(), }) .unwrap(); diff --git a/examples/d3d12-visualization/main.rs b/examples/d3d12-visualization/main.rs index e9bb1849..a364a786 100644 --- a/examples/d3d12-visualization/main.rs +++ b/examples/d3d12-visualization/main.rs @@ -358,6 +358,7 @@ fn main() { let mut allocator = Allocator::new(&AllocatorCreateDesc { device: device.as_windows().clone(), debug_settings: Default::default(), + allocation_sizes: Default::default(), }) .unwrap(); diff --git a/examples/vulkan-buffer.rs b/examples/vulkan-buffer.rs index 3a5e3762..b2175de9 100644 --- a/examples/vulkan-buffer.rs +++ b/examples/vulkan-buffer.rs @@ -98,6 +98,7 @@ fn main() { physical_device: pdevice, debug_settings: Default::default(), buffer_device_address: false, + allocation_sizes: Default::default(), }) .unwrap(); diff --git a/examples/vulkan-visualization/main.rs b/examples/vulkan-visualization/main.rs index de3cc42e..652a11d2 100644 --- a/examples/vulkan-visualization/main.rs +++ b/examples/vulkan-visualization/main.rs @@ -231,6 +231,7 @@ fn main() -> ash::prelude::VkResult<()> { physical_device: pdevice, debug_settings: Default::default(), buffer_device_address: false, + allocation_sizes: Default::default(), }) .unwrap(), ); diff --git a/src/d3d12/mod.rs b/src/d3d12/mod.rs index b02216ba..2a77b603 100644 --- a/src/d3d12/mod.rs +++ b/src/d3d12/mod.rs @@ -85,7 +85,8 @@ use super::allocator; use super::allocator::AllocationType; use crate::{ - allocator::fmt_bytes, AllocationError, AllocatorDebugSettings, MemoryLocation, Result, + allocator::fmt_bytes, AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation, + Result, }; /// [`ResourceCategory`] is used for supporting [`D3D12_RESOURCE_HEAP_TIER_1`]. @@ -231,6 +232,7 @@ impl<'a> AllocationCreateDesc<'a> { pub struct AllocatorCreateDesc { pub device: ID3D12Device, pub debug_settings: AllocatorDebugSettings, + pub allocation_sizes: AllocationSizes, } pub enum ResourceType<'a> { @@ -386,21 +388,20 @@ struct MemoryType { active_general_blocks: usize, } -const DEFAULT_DEVICE_MEMBLOCK_SIZE: u64 = 256 * 1024 * 1024; -const DEFAULT_HOST_MEMBLOCK_SIZE: u64 = 64 * 1024 * 1024; impl MemoryType { fn allocate( &mut self, device: &ID3D12Device, desc: &AllocationCreateDesc<'_>, backtrace: Option, + allocation_sizes: &AllocationSizes, ) -> Result { let allocation_type = AllocationType::Linear; let memblock_size = if self.heap_properties.Type == D3D12_HEAP_TYPE_DEFAULT { - DEFAULT_DEVICE_MEMBLOCK_SIZE + allocation_sizes.device_memblock_size } else { - DEFAULT_HOST_MEMBLOCK_SIZE + allocation_sizes.host_memblock_size }; let size = desc.size; @@ -573,6 +574,7 @@ pub struct Allocator { device: ID3D12Device, debug_settings: AllocatorDebugSettings, memory_types: Vec, + allocation_sizes: AllocationSizes, } impl Allocator { @@ -679,6 +681,7 @@ impl Allocator { memory_types, device, debug_settings: desc.debug_settings, + allocation_sizes: desc.allocation_sizes, }) } @@ -722,7 +725,7 @@ impl Allocator { }) .ok_or(AllocationError::NoCompatibleMemoryTypeFound)?; - memory_type.allocate(&self.device, desc, backtrace) + memory_type.allocate(&self.device, desc, backtrace, &self.allocation_sizes) } pub fn free(&mut self, allocation: Allocation) -> Result<()> { diff --git a/src/lib.rs b/src/lib.rs index 04096d20..f23acc94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ //! physical_device, //! debug_settings: Default::default(), //! buffer_device_address: true, // Ideally, check the BufferDeviceAddressFeatures struct. +//! allocation_sizes: Default::default(), //! }); //! # } //! # #[cfg(not(feature = "vulkan"))] @@ -48,6 +49,7 @@ //! # physical_device, //! # debug_settings: Default::default(), //! # buffer_device_address: true, // Ideally, check the BufferDeviceAddressFeatures struct. +//! # allocation_sizes: Default::default(), //! # }).unwrap(); //! //! // Setup vulkan info @@ -89,6 +91,7 @@ //! let mut allocator = Allocator::new(&AllocatorCreateDesc { //! device, //! debug_settings: Default::default(), +//! allocation_sizes: Default::default(), //! }); //! # } //! # #[cfg(not(feature = "d3d12"))] @@ -108,6 +111,7 @@ //! # let mut allocator = Allocator::new(&AllocatorCreateDesc { //! # device: device, //! # debug_settings: Default::default(), +//! # allocation_sizes: Default::default(), //! # }).unwrap(); //! //! let buffer_desc = Direct3D12::D3D12_RESOURCE_DESC { @@ -210,3 +214,58 @@ impl Default for AllocatorDebugSettings { } } } + +/// 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, + } + } +} diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 2d0760a8..be90c930 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -13,7 +13,8 @@ use log::{debug, Level}; use std::fmt; use crate::{ - allocator::fmt_bytes, AllocationError, AllocatorDebugSettings, MemoryLocation, Result, + allocator::fmt_bytes, AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation, + Result, }; #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -61,6 +62,7 @@ pub struct AllocatorCreateDesc { pub physical_device: ash::vk::PhysicalDevice, pub debug_settings: AllocatorDebugSettings, pub buffer_device_address: bool, + pub allocation_sizes: AllocationSizes, } /// A piece of allocated memory. @@ -441,9 +443,6 @@ pub(crate) struct MemoryType { pub(crate) buffer_device_address: bool, } -const DEFAULT_DEVICE_MEMBLOCK_SIZE: u64 = 256 * 1024 * 1024; -const DEFAULT_HOST_MEMBLOCK_SIZE: u64 = 64 * 1024 * 1024; - impl MemoryType { fn allocate( &mut self, @@ -451,6 +450,7 @@ impl MemoryType { desc: &AllocationCreateDesc<'_>, granularity: u64, backtrace: Option, + allocation_sizes: &AllocationSizes, ) -> Result { let allocation_type = if desc.linear { AllocationType::Linear @@ -462,9 +462,9 @@ impl MemoryType { .memory_properties .contains(vk::MemoryPropertyFlags::HOST_VISIBLE) { - DEFAULT_HOST_MEMBLOCK_SIZE + allocation_sizes.host_memblock_size } else { - DEFAULT_DEVICE_MEMBLOCK_SIZE + allocation_sizes.device_memblock_size }; let size = desc.requirements.size; @@ -677,6 +677,7 @@ pub struct Allocator { device: ash::Device, pub(crate) buffer_image_granularity: u64, pub(crate) debug_settings: AllocatorDebugSettings, + allocation_sizes: AllocationSizes, } impl fmt::Debug for Allocator { @@ -793,6 +794,7 @@ impl Allocator { device: desc.device.clone(), buffer_image_granularity: granularity, debug_settings: desc.debug_settings, + allocation_sizes: AllocationSizes::default(), }) } @@ -866,6 +868,7 @@ impl Allocator { desc, self.buffer_image_granularity, backtrace.clone(), + &self.allocation_sizes, ) }; @@ -887,6 +890,7 @@ impl Allocator { desc, self.buffer_image_granularity, backtrace, + &self.allocation_sizes, ) } else { allocation