nobs_vkmem/lib.rs
1//! Vulkan memory management as extension to [nobs-vk](https://docs.rs/nobs-vk).
2//!
3//! Buffer and image creation in vulkan is tricky in comparison to e.g. OpenGL, because
4//! 1. We have to create the buffer/image and then later bind it to a `vkDeviceMemory` that has to be created separately.
5//! 2. Creating a single `vkDeviceMemory` allocation for every buffer/image is bad practice,
6//! in fact it is [encouraged](https://developer.nvidia.com/vulkan-memory-management) to bind esources that are used together on the same allocation.
7//! 3. Another layer of difficulty is introduced with memory types, since not all resources (should) share the same memory properties - which is different for each Driver/Vendor
8//!
9//! nobs-vkmem provides convenient and accessible methods for creating buffers and images and binding them to physical memory.
10//! This dramatically reduces boiler plate code, while still offers the user considerable control over how resources are bound to memory.
11//! 1. Easy buffer and image creation with builder patterns.
12//! 2. Device memory is allocated in larger pages. The library keeps track of free and used regions in a page.
13//! 3. Offers different allocation strategies for different purposes, including forcing the binding of several resources to a continuous block, or binding resources on private pages.
14//! 4. Easy mapping of host accessible buffers
15//!
16//! Interfacing with this library is mainly handled in [Allocator](struct.Allocator.html), with which buffers and images are bound to device memory.
17//!
18//! [Buffer](builder/struct.Buffer.html) and [Image](builder/struct.Image.html) provide a convenient way to configure buffers/images and bind them to the allocator in bulk.
19//!
20//! See [Allocator](struct.Allocator.html) to get a quick overview on how to use this library.
21#[macro_use]
22extern crate nobs_vk as vk;
23
24mod block;
25mod builder;
26mod mapped;
27mod page;
28
29pub use builder::Buffer;
30pub use builder::Image;
31pub use builder::ImageView;
32pub use builder::Resource;
33pub use mapped::Mapped;
34pub use page::BindType;
35
36use std::collections::HashMap;
37use std::fmt::Write;
38
39use block::Block;
40use page::PageTable;
41
42/// Errors that can be occure when using this crate
43#[derive(Debug)]
44pub enum Error {
45 /// Indicates, that the desired pagesize is too small.
46 /// Pages need to be at least of size `bufferImageGranularity`,
47 /// that is defined in the physical device limits (or with [get_min_pagesize](struct.Allocator.html#method.get_min_pagesize)).
48 InvalidPageSize,
49 /// Indicates, that the allocator could not allocate a new page.
50 AllocError,
51 /// Indicates, that there is not enough free space available to bind resources.
52 OutOfMemory,
53 /// Indicates that the requested resources can not be bound to a single block, because they are larger than a page.
54 OversizedBlock,
55 /// Indicates, that this device does not have a memory type, that satisfies a combination of 'vk::MemoryRequirements' and 'vk::MemoryPropertyFlags'.
56 InvalidMemoryType,
57 /// Indicates, that a buffer create returned unsuccessfull.
58 /// The wrapped value is the index of the resource that could not be created.
59 CreateBufferFailed(u32),
60 /// Indicates, that an image create returned unsuccessfull.
61 /// The wrapped value is the index of the resource that could not be created.
62 CreateImageFailed(u32),
63 /// Indicates, that binding a buffer or image failed
64 BindMemoryFailed,
65 /// Indicates, that a resource was bound multiple times
66 AlreadyBound,
67 /// indicates, that the requested memory region could not be mapped
68 MapError,
69}
70
71/// Enum defining resource types that can be bound
72///
73/// Handles that can be bound to memory are either of type vk::Buffer or vk::Image.
74/// In both cases the handles are u64 typedefs.
75/// The enum in used, so that we can submit buffer and image handles for binding to the Allocator in a uniform fashion,
76/// whlile still being able to distinguish between them.
77///
78/// Submitting buffers along with images to the [Allocator](struct.Allocator.html) makes sence, when they use the same memory type
79/// (which is the case for device local buffers and images)
80#[derive(Debug, Clone, Copy)]
81pub enum Handle<T>
82where
83 T: Clone + Copy,
84{
85 Buffer(T),
86 Image(T),
87}
88
89impl<T> Handle<T>
90where
91 T: Clone + Copy,
92{
93 /// Gets the underlying handle's value
94 pub fn get(&self) -> T {
95 match self {
96 Handle::Image(h) => *h,
97 Handle::Buffer(h) => *h,
98 }
99 }
100
101 /// Convert the value of the handle without changing it's enum type
102 fn map<U: Clone + Copy>(self, u: U) -> Handle<U> {
103 match self {
104 Handle::Image(_) => Handle::Image(u),
105 Handle::Buffer(_) => Handle::Buffer(u),
106 }
107 }
108}
109
110/// Bundles all information for the [Allocator](struct.Allocator.html) to perform a resource memory binding.
111///
112/// It is possible to additionally specify the size of the resource in bytes ([with_size](struct.BindInfo.html#method.with_size)).
113/// This size only matters, if the resource is a buffer that will be mapped into host accessible memory.
114/// If no size is specified with the constructor the size for the memory binding will be retrieved from the buffer's `vk::MemoryRequirements`,
115/// which might include a padding at the end. If the buffer is then mapped with [get_mapped](struct.Allocator.html#method.get_mapped) the retured
116/// [Mapped](mapped/struct.Mapped.html) will contain the buffer including padding. By specifying an explicit size this can be circumvented.
117///
118/// When the builders [Buffer](builder/struct.Buffer.html) and [Image](builder/struct.Image.html) are used resources will allways be submitted with their actual size.
119#[derive(Debug, Clone, Copy)]
120pub struct BindInfo {
121 handle: Handle<u64>,
122 size: Option<vk::DeviceSize>,
123 properties: vk::MemoryPropertyFlags,
124}
125
126impl BindInfo {
127 /// Create BindInfo from the specified memory properties
128 ///
129 /// ## Arguments
130 /// *`handle` - the handle that needs a memory binding
131 /// *`properties` - the memory properties indicating if the resource is device local or host accessible
132 pub fn new(handle: Handle<u64>, properties: vk::MemoryPropertyFlags) -> Self {
133 Self {
134 handle,
135 properties,
136 size: None,
137 }
138 }
139
140 /// Create BindInfo with the specified memory properties and explicit size in bytes
141 ///
142 /// ## Arguments
143 /// *`handle` - the handle that needs a memory binding
144 /// *`size` - the actual size of the resource (in bytes)
145 /// *`properties` - the memory properties indicating if the resource is device local or host accessible
146 pub fn with_size(handle: Handle<u64>, size: vk::DeviceSize, properties: vk::MemoryPropertyFlags) -> Self {
147 Self {
148 handle,
149 properties,
150 size: Some(size),
151 }
152 }
153}
154
155/// Defines meta information for the [Allocator](struct.Allocator.html)
156///
157/// Caches `vk::MemoryRequirements` for buffers and images.
158/// Defines a pagesize for memory types of all common combinations of resource types [buffer, image] and memory properties [device local, host accessible].
159#[derive(Debug)]
160pub struct AllocatorSizes {
161 /// Handle to the physical device
162 ///
163 /// Used to retrieve and check against device limits
164 pub pdevice: vk::PhysicalDevice,
165 /// Cached memory requirements for image resourcses
166 ///
167 /// Used to get the memory type index without having to create an image.
168 pub image_requirements: vk::MemoryRequirements,
169 /// Cached memory requirements for image resourcses
170 ///
171 /// Used to get the memory type index without having to create a buffer.
172 pub buffer_requirements: vk::MemoryRequirements,
173
174 /// Default page size in bytes
175 ///
176 /// This is the fallback page size, that is returned in [get_pagesize](struct.AllocatorSizes.html#method.get_pagesize), if no
177 /// mapping for the requested memory type index exists.
178 ///
179 /// The default page size is initialized with 64MiB.
180 pub pagesize_default: vk::DeviceSize,
181 /// Page size mapped by memory type index
182 pub pagesizes: HashMap<u32, vk::DeviceSize>,
183}
184
185impl AllocatorSizes {
186 /// Creates an AllocatorSizes object with default pagesizes
187 ///
188 /// Initializes pagesizes for the allocator with
189 /// - 128MiB for device local resources
190 /// - 8MiB for host accessible resources
191 /// - 64MiB fallback pagesize
192 ///
193 /// # Arguments
194 /// * `pdevice` - physical device handle
195 /// * `device` - device handle
196 ///
197 pub fn new(pdevice: vk::PhysicalDevice, device: vk::Device) -> Self {
198 let image_requirements = {
199 let info = vk::ImageCreateInfo {
200 sType: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
201 pNext: std::ptr::null(),
202 flags: 0,
203 imageType: vk::IMAGE_TYPE_2D,
204 extent: vk::Extent3D {
205 width: 1,
206 height: 1,
207 depth: 1,
208 },
209 mipLevels: 1,
210 arrayLayers: 1,
211 tiling: vk::IMAGE_TILING_OPTIMAL,
212 format: vk::FORMAT_R8G8B8A8_SRGB,
213 initialLayout: vk::IMAGE_LAYOUT_UNDEFINED,
214 usage: vk::IMAGE_USAGE_TRANSFER_DST_BIT | vk::IMAGE_USAGE_SAMPLED_BIT,
215 samples: vk::SAMPLE_COUNT_1_BIT,
216 sharingMode: vk::SHARING_MODE_EXCLUSIVE,
217 queueFamilyIndexCount: 0,
218 pQueueFamilyIndices: std::ptr::null(),
219 };
220 let mut handle = vk::NULL_HANDLE;
221 vk_uncheck!(vk::CreateImage(device, &info, std::ptr::null(), &mut handle));
222
223 let mut requirements = unsafe { std::mem::uninitialized() };
224 vk::GetImageMemoryRequirements(device, handle, &mut requirements);
225
226 vk::DestroyImage(device, handle, std::ptr::null());
227
228 requirements
229 };
230 let buffer_requirements = {
231 let info = vk::BufferCreateInfo {
232 sType: vk::STRUCTURE_TYPE_BUFFER_CREATE_INFO,
233 pNext: std::ptr::null(),
234 flags: 0,
235 size: 1,
236 usage: vk::BUFFER_USAGE_TRANSFER_SRC_BIT
237 | vk::BUFFER_USAGE_TRANSFER_DST_BIT
238 | vk::BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
239 | vk::BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT
240 | vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT
241 | vk::BUFFER_USAGE_STORAGE_BUFFER_BIT
242 | vk::BUFFER_USAGE_INDEX_BUFFER_BIT
243 | vk::BUFFER_USAGE_VERTEX_BUFFER_BIT
244 | vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT,
245 sharingMode: vk::SHARING_MODE_EXCLUSIVE,
246 queueFamilyIndexCount: 0,
247 pQueueFamilyIndices: std::ptr::null(),
248 };
249 let mut handle = vk::NULL_HANDLE;
250 vk_uncheck!(vk::CreateBuffer(device, &info, std::ptr::null(), &mut handle));
251
252 let mut requirements = unsafe { std::mem::uninitialized() };
253 vk::GetBufferMemoryRequirements(device, handle, &mut requirements);
254
255 vk::DestroyBuffer(device, handle, std::ptr::null());
256
257 requirements
258 };
259
260 let minsize = Allocator::get_min_pagesize(pdevice);
261 let pagesize_default = vk::DeviceSize::max(minsize * 4, 1 << 26);
262 let mut pagesizes = HashMap::new();
263
264 let memtype_local_image = Allocator::get_memtype(pdevice, &image_requirements, vk::MEMORY_PROPERTY_DEVICE_LOCAL_BIT).unwrap();
265 let memtype_local_buffer = Allocator::get_memtype(pdevice, &buffer_requirements, vk::MEMORY_PROPERTY_DEVICE_LOCAL_BIT).unwrap();
266 let memtype_hostaccess = Allocator::get_memtype(
267 pdevice,
268 &buffer_requirements,
269 vk::MEMORY_PROPERTY_HOST_VISIBLE_BIT | vk::MEMORY_PROPERTY_HOST_COHERENT_BIT,
270 )
271 .unwrap();
272
273 pagesizes.insert(memtype_local_image, vk::DeviceSize::max(minsize, 1 << 27));
274 pagesizes.insert(memtype_local_buffer, vk::DeviceSize::max(minsize, 1 << 27));
275 if memtype_local_buffer != memtype_hostaccess {
276 pagesizes.insert(memtype_hostaccess, vk::DeviceSize::max(minsize, 1 << 18));
277 }
278
279 Self {
280 pagesize_default,
281 pagesizes,
282
283 pdevice,
284 image_requirements,
285 buffer_requirements,
286 }
287 }
288
289 /// Get the memtype of an image with the specified memory properties
290 ///
291 /// ## Returns
292 /// - An option with the memory type index.
293 /// - None, if there is no memory type that supports images with the specified properties
294 pub fn get_image_memtype(&self, properties: vk::MemoryPropertyFlags) -> Option<u32> {
295 Allocator::get_memtype(self.pdevice, &self.image_requirements, properties)
296 }
297
298 /// Get the memtype of a buffer with the specified memory properties
299 ///
300 /// ## Returns
301 /// - An option with the memory type index.
302 /// - None, if there is no memory type that supports buffers with the specified properties
303 pub fn get_buffer_memtype(&self, properties: vk::MemoryPropertyFlags) -> Option<u32> {
304 Allocator::get_memtype(self.pdevice, &self.buffer_requirements, properties)
305 }
306
307 /// Get the pagesize for the memory type
308 ///
309 /// # Returns
310 /// - The pagesize of the memory type, if one has been set in the AllocatorSizes.
311 /// - Otherwise the default pagesize is returned.
312 pub fn get_pagesize(&self, memtype: u32) -> vk::DeviceSize {
313 match self.pagesizes.get(&memtype) {
314 Some(size) => *size,
315 None => self.pagesize_default,
316 }
317 }
318
319 /// Set the pagesize for the specifid memory type
320 ///
321 /// The page size must be at least of size `bufferImageGranularity`. If not the result returns [InvalidPageSize](enum.Error.html#field.InvalidPageSize).
322 pub fn set_pagesize(&mut self, memtype: u32, size: vk::DeviceSize) -> Result<(), Error> {
323 if size < Allocator::get_min_pagesize(self.pdevice) {
324 Err(Error::InvalidPageSize)?
325 }
326 self.pagesizes.entry(memtype).or_insert(size);
327 Ok(())
328 }
329}
330
331/// Allocator for buffer and image resources
332///
333/// Manages device memory for a single device. The actual memory is managed for each memory type separately by a page table.
334/// Pages are allocated lazyly, as soon as memory is needed. The pagesizes may be specified in the [AllocatorSizes](struct.AllocatorSizes.html).
335///
336/// When the Allocator is dropped, all buffers and allocated device momory is freed.
337///
338/// # Example
339/// The Allocator is created from a device handle and it's associated physical device.
340/// Buffers and images can be easyly created with the [Buffer](builder/struct.Buffer.html) and [Image](builder/struct.Image.html) builder.
341/// Host accessible buffers can be conveniently accessed with [get_mapped / get_mapped_region](struct.Allocator.html#method.get_mapped) and [Mapped](mapped/struct.Mapped.html).
342/// Allocated resources may be freed with [destroy(_many)](struct.Allocator.html#method.destroy).
343/// Memory of pages that have no bore resource binding can be freed with [free_unused](struct.Allocator.html#method.free_unused) again.
344///
345/// ```rust
346/// extern crate nobs_vk as vk;
347/// extern crate nobs_vkmem as vkmem;
348///
349/// struct Ub {
350/// a: u32,
351/// b: u32,
352/// c: u32,
353/// }
354///
355/// # fn main() {
356/// # let lib = vk::VkLib::new();
357/// # let inst = vk::instance::new()
358/// # .validate(vk::DEBUG_REPORT_ERROR_BIT_EXT | vk::DEBUG_REPORT_WARNING_BIT_EXT)
359/// # .application("awesome app", 0)
360/// # .add_extension(vk::KHR_SURFACE_EXTENSION_NAME)
361/// # .add_extension(vk::KHR_XLIB_SURFACE_EXTENSION_NAME)
362/// # .create(lib)
363/// # .unwrap();
364/// # let (pdevice, device) = vk::device::PhysicalDevice::enumerate_all(inst.handle)
365/// # .remove(0)
366/// # .into_device()
367/// # .add_queue(vk::device::QueueProperties {
368/// # present: false,
369/// # graphics: true,
370/// # compute: true,
371/// # transfer: true,
372/// # }).create()
373/// # .unwrap();
374///
375/// // create an allocator with default page size
376/// // (128MiB for device local / 8MB for host visible memory)
377/// let mut allocator = vkmem::Allocator::new(pdevice.handle, device.handle);
378///
379/// // declare handles
380/// let mut buf_ub = vk::NULL_HANDLE;
381/// let mut buf_out = vk::NULL_HANDLE;
382/// let mut img = vk::NULL_HANDLE;
383/// let mut bb = vk::NULL_HANDLE;
384///
385/// // configure create infos
386/// vkmem::Buffer::new(&mut buf_ub)
387/// .size(std::mem::size_of::<Ub>() as vk::DeviceSize)
388/// .usage(vk::BUFFER_USAGE_TRANSFER_DST_BIT | vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT)
389/// .devicelocal(false)
390/// .new_buffer(&mut buf_out)
391/// .size(123 * std::mem::size_of::<u32>() as vk::DeviceSize)
392/// .usage(vk::BUFFER_USAGE_TRANSFER_DST_BIT | vk::BUFFER_USAGE_STORAGE_BUFFER_BIT)
393/// .devicelocal(false)
394/// .new_image(&mut img)
395/// .image_type(vk::IMAGE_TYPE_2D)
396/// .size(123, 123, 1)
397/// .usage(vk::IMAGE_USAGE_SAMPLED_BIT)
398/// .devicelocal(true)
399/// .new_buffer(&mut bb)
400/// .size(123 * std::mem::size_of::<u32>() as vk::DeviceSize)
401/// .usage(vk::BUFFER_USAGE_TRANSFER_DST_BIT | vk::BUFFER_USAGE_STORAGE_BUFFER_BIT)
402/// .devicelocal(true)
403///
404/// // this will create the image/buffers and bind them to memory
405/// .bind(&mut allocator, vkmem::BindType::Scatter)
406/// .unwrap();
407///
408/// // Mapped gives a convenient view on the memory
409/// // get_mapped mapps the whole block of memory to which the resources was bound
410/// // get_mapped_region lets us define a byte offset respective to the beginning of the resource and a size in bytes
411/// {
412/// let mapped = allocator.get_mapped(buf_ub).unwrap();
413/// let ubb = Ub { a: 123, b: 4, c: 5 };
414/// mapped.host_to_device(&ubb);
415/// }
416/// {
417/// let mapped = allocator.get_mapped(buf_ub).unwrap();
418/// let mut ubb = Ub { a: 0, b: 0, c: 0 };
419/// mapped.device_to_host(&mut ubb);
420/// }
421///
422/// {
423/// let mapped = allocator.get_mapped_region(buf_out, 4, 100).unwrap();
424/// let v = mapped.as_slice::<u32>();
425/// }
426///
427/// // we can print stats in a yaml format for the currently allocated pages
428/// println!("{}", allocator.print_stats());
429///
430/// // buffers and images can be destroyed
431/// allocator.destroy(img);
432/// allocator.destroy_many(&[buf_ub, buf_out]);
433///
434/// // destroying does NOT free memory
435/// // if we want to free memory we can do this only if a whole page is not used any more
436/// // in this case we can free the memory again
437/// allocator.free_unused();
438///
439/// // dropping the allocator automatically destroys bound resources and frees all memory
440/// # }
441/// ```
442pub struct Allocator {
443 device: vk::Device,
444 sizes: AllocatorSizes,
445
446 pagetbls: HashMap<u32, page::PageTable>,
447 handles: HashMap<u64, Handle<u32>>,
448}
449
450impl Drop for Allocator {
451 fn drop(&mut self) {
452 for (h, mt) in self.handles.iter() {
453 match mt {
454 Handle::Buffer(_) => vk::DestroyBuffer(self.device, *h, std::ptr::null()),
455 Handle::Image(_) => vk::DestroyImage(self.device, *h, std::ptr::null()),
456 };
457 }
458 }
459}
460
461impl Allocator {
462 /// Gets the smallest pagesize for the specified physical device
463 ///
464 /// The minimum page size is defined through the `bufferImageGranularity` of the physical device properties
465 ///
466 /// # Arguments
467 /// * `pdevice` - physical device handle
468 ///
469 /// # Returns
470 /// The minimum pagesize in bytes.
471 pub fn get_min_pagesize(pdevice: vk::PhysicalDevice) -> vk::DeviceSize {
472 let mut properties: vk::PhysicalDeviceProperties = unsafe { std::mem::uninitialized() };
473 vk::GetPhysicalDeviceProperties(pdevice, &mut properties);
474 properties.limits.bufferImageGranularity
475 }
476
477 /// Get the memtype for a combination of memory requirements and properties
478 ///
479 /// # Arguments
480 /// * `pdevice` - physical device handle
481 /// * `requirements` - memory reqirements retrieved with `vk::GetBufferMemoryRequirements` or `vk::GetBufferMemoryRequirements`
482 /// * `properties` - combination of `vk::MemoryPropertyFlagBits`
483 ///
484 /// # Returns
485 /// * `Some(memtype)` - where `memtype` is the memory type index
486 /// * `None` - if there no such a combination of memory requirements and properties exists on the physical device
487 pub fn get_memtype(
488 pdevice: vk::PhysicalDevice,
489 requirements: &vk::MemoryRequirements,
490 properties: vk::MemoryPropertyFlags,
491 ) -> Option<u32> {
492 let mut device_properties = unsafe { std::mem::uninitialized() };
493 vk::GetPhysicalDeviceMemoryProperties(pdevice, &mut device_properties);
494
495 (0..device_properties.memoryTypeCount)
496 .into_iter()
497 .position(|i| {
498 (requirements.memoryTypeBits & (1 << i)) != 0
499 && (device_properties.memoryTypes[i as usize].propertyFlags & properties) == properties
500 })
501 .map(|i| i as u32)
502 }
503
504 /// Creates an Allocator
505 ///
506 /// The Allocator will use the default AllocatorSizes, with 128MiB / 8MiB pagesizes for device local / host accessible memory.
507 ///
508 /// # Arguments
509 /// * `pdevice` - physical device handle
510 /// * `device` - device handle
511 pub fn new(pdevice: vk::PhysicalDevice, device: vk::Device) -> Allocator {
512 Self::with_sizes(device, AllocatorSizes::new(pdevice, device))
513 }
514
515 /// Creates an Allocator from AllocatorSizes
516 ///
517 /// The AllocatorSizes will consumed by the consturctor.
518 /// This allows additional configuration of the pagesizes for every memory type.
519 ///
520 /// # Arguments
521 /// * `device` - device handle
522 /// * `sizes` - AllocatorSizes with the physical device handle and pagesize definitions
523 ///
524 /// # Example
525 /// Creates an Allocator with 32MiB large pages for host accassable memory
526 /// ```rust
527 /// # extern crate nobs_vk as vk;
528 /// # extern crate nobs_vkmem as vkmem;
529 /// # fn main() {
530 /// # let lib = vk::VkLib::new();
531 /// # let inst = vk::instance::new()
532 /// # .validate(vk::DEBUG_REPORT_ERROR_BIT_EXT | vk::DEBUG_REPORT_WARNING_BIT_EXT)
533 /// # .application("awesome app", 0)
534 /// # .add_extension(vk::KHR_SURFACE_EXTENSION_NAME)
535 /// # .add_extension(vk::KHR_XLIB_SURFACE_EXTENSION_NAME)
536 /// # .create(lib)
537 /// # .unwrap();
538 /// # let (pdevice, device) = vk::device::PhysicalDevice::enumerate_all(inst.handle)
539 /// # .remove(0)
540 /// # .into_device()
541 /// # .add_queue(vk::device::QueueProperties {
542 /// # present: false,
543 /// # graphics: true,
544 /// # compute: true,
545 /// # transfer: true,
546 /// # }).create()
547 /// # .unwrap();
548 /// let mut sizes = vkmem::AllocatorSizes::new(pdevice.handle, device.handle);
549 /// if let Some(memtype) = sizes.get_buffer_memtype(vk::MEMORY_PROPERTY_HOST_VISIBLE_BIT | vk::MEMORY_PROPERTY_HOST_COHERENT_BIT) {
550 /// sizes.set_pagesize(memtype, 1 << 25);
551 /// }
552 /// let mut allocator = vkmem::Allocator::with_sizes(pdevice.handle, sizes);
553 /// # }
554 /// ```
555 pub fn with_sizes(device: vk::Device, sizes: AllocatorSizes) -> Allocator {
556 Allocator {
557 device,
558 sizes,
559 pagetbls: Default::default(),
560 handles: Default::default(),
561 }
562 }
563
564 /// Bind resources to device memory
565 ///
566 /// This will automatically allocate memory, if necessary.
567 ///
568 /// Resources that are used together shoulb be bound in the same call to this function,
569 /// since it tries to minimize the number of continuous memory blocks on which the resources are bound.
570 /// Predominatly this is important after resources have been deleted and free blocks of memory are distributed over multiple pages.
571 ///
572 /// We can specify a rough strategy of how resources are bound with the `bindtype` parameter. See [BindType](page/enum.BindType.html) for the different strategies
573 ///
574 /// The Allocator takes ownorship ower the resource handles.
575 ///
576 /// Note that it is not neccesary that all [BindInfos](struct.BindInfo.html) need to specify resources that are allocated on the same memory type.
577 /// The Allocator will detangle the BindInfos automatically and generate one binding for every group of resources on the same memory type.
578 ///
579 /// # Arguments
580 /// * `bindinfos` - Array of [BindInfo](struct.BindInfo.html) that specifies the resources to be bound
581 /// * `bindtype` - Allocation strategy
582 ///
583 /// # Returns
584 /// [Error](enum.Error.html) if the allocator failed to bind one or more resources:
585 /// * `Error::AllocError` - if a new page could not be allocated
586 /// * `Error::OutOfMemory` - if not enough memory is available to bind all resources
587 /// * `Error::OversizedBlock` - if a single resourcse is larger than it's associated pagesize,
588 /// or if `bindtype` is `BindType::Block` and the combined size of all resources is larger than their associated pagesize
589 /// * `Error::InvalidMemoryType` - if for one or more resources no memory type is found that satisfies the memory requirements and properties
590 /// * `Error::BindMemoryFailed` - if one or more resources could not be bound to device memory
591 /// * `Error::AlreadyBound` - if one or more resources are already bound to device memory
592 ///
593 /// # Example
594 /// Shows how buffers and images can be bound to the allocator. Prior to bind the resources have to be created with either `vk::CreateBuffer` or `vk::CreateImage`.
595 ///
596 /// The BindInfo can be generated manually (as in the example),
597 /// see [Buffer](builder/struct.Buffer.html) and [Image](builder/struct.Image.html) for convenient configuration and binding of buffers and images
598 ///
599 /// ```rust,no_run
600 /// # extern crate nobs_vk as vk;
601 /// # extern crate nobs_vkmem as vkmem;
602 /// # fn main() {
603 /// # let lib = vk::VkLib::new();
604 /// # let inst = vk::instance::new()
605 /// # .validate(vk::DEBUG_REPORT_ERROR_BIT_EXT | vk::DEBUG_REPORT_WARNING_BIT_EXT)
606 /// # .application("awesome app", 0)
607 /// # .create(lib)
608 /// # .unwrap();
609 /// # let (pdevice, device) = vk::device::PhysicalDevice::enumerate_all(inst.handle)
610 /// # .remove(0)
611 /// # .into_device()
612 /// # .add_queue(vk::device::QueueProperties {
613 /// # present: false,
614 /// # graphics: true,
615 /// # compute: true,
616 /// # transfer: true,
617 /// # }).create()
618 /// # .unwrap();
619 /// let mut allocator = vkmem::Allocator::new(pdevice.handle, device.handle);
620 ///
621 /// let buf = vk::NULL_HANDLE;
622 /// let img = vk::NULL_HANDLE;
623 /// // ... create buffers/images
624 ///
625 /// let buf = vkmem::Handle::Buffer(buf);
626 /// let img = vkmem::Handle::Image(img);
627 /// let host_access_flags =
628 /// vk::MEMORY_PROPERTY_HOST_VISIBLE_BIT |
629 /// vk::MEMORY_PROPERTY_HOST_COHERENT_BIT;
630 /// let device_local_flags = vk::MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
631 ///
632 /// allocator.bind(
633 /// &[
634 /// vkmem::BindInfo::with_size(buf, 12, host_access_flags),
635 /// vkmem::BindInfo::new(img, device_local_flags),
636 /// ],
637 /// vkmem::BindType::Scatter,
638 /// ).expect("binding buffers failed");
639 /// # }
640 /// ```
641 pub fn bind(&mut self, bindinfos: &[BindInfo], bindtype: BindType) -> Result<(), Error> {
642 if bindinfos.is_empty() {
643 return Ok(());
644 }
645
646 // sort handles into groups of the same memory type
647 let mut by_memtype = HashMap::new();
648 for info in bindinfos.iter() {
649 let pageinfo = page::BindInfo::new(self.device, info);
650 let memtype = Self::get_memtype(self.sizes.pdevice, &pageinfo.requirements, info.properties).ok_or(Error::InvalidMemoryType)?;
651 by_memtype.entry(memtype).or_insert(Vec::new()).push(pageinfo);
652 }
653
654 // for every group with the same memtype bind the buffers to a page table
655 let device = self.device;
656 for (memtype, infos) in by_memtype {
657 let pagesize = self.sizes.get_pagesize(memtype);
658 self
659 .pagetbls
660 .entry(memtype)
661 .or_insert_with(|| PageTable::new(device, memtype, pagesize))
662 .bind(&infos, bindtype)?;
663
664 for h in infos.iter().map(|i| i.handle) {
665 self.handles.insert(h.get(), h.map(memtype));
666 }
667 }
668
669 Ok(())
670 }
671
672 /// Destroys a resource, that has been bound to this allocator
673 ///
674 /// see [destroy_many](struct.Allocator.html#method.destroy_many)
675 pub fn destroy(&mut self, handle: u64) {
676 self.destroy_many(&[handle]);
677 }
678
679 /// Destroys resources, that have been bound to this allocator
680 ///
681 /// Destroys the buffer or image and makes their associated memory available again.
682 ///
683 /// Freeing the memory is relatively expensive compared to allocation, which is why it should be preferred to use `destroy_many` over just `destroy`.
684 /// When multiple resources are destroyed in bulk, merging blocks of free memory and padding together with the freed allocation has to be done only once.
685 ///
686 /// Note that memory bindings of not destroyed resources can not be rearranged, since vulkan does not allow rebinding a buffer/image to a different location.
687 ///
688 /// # Example
689 /// ```rust
690 /// # extern crate nobs_vk as vk;
691 /// # extern crate nobs_vkmem as vkmem;
692 /// # fn main() {
693 /// # let lib = vk::VkLib::new();
694 /// # let inst = vk::instance::new()
695 /// # .validate(vk::DEBUG_REPORT_ERROR_BIT_EXT | vk::DEBUG_REPORT_WARNING_BIT_EXT)
696 /// # .application("awesome app", 0)
697 /// # .create(lib)
698 /// # .unwrap();
699 /// # let (pdevice, device) = vk::device::PhysicalDevice::enumerate_all(inst.handle)
700 /// # .remove(0)
701 /// # .into_device()
702 /// # .add_queue(vk::device::QueueProperties {
703 /// # present: false,
704 /// # graphics: true,
705 /// # compute: true,
706 /// # transfer: true,
707 /// # }).create()
708 /// # .unwrap();
709 /// let mut allocator = vkmem::Allocator::new(pdevice.handle, device.handle);
710 ///
711 /// let mut buf = vk::NULL_HANDLE;
712 /// let mut img = vk::NULL_HANDLE;
713 /// # vkmem::Buffer::new(&mut buf)
714 /// # .size(12)
715 /// # .usage(vk::BUFFER_USAGE_TRANSFER_DST_BIT | vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT)
716 /// # .devicelocal(false)
717 /// # .new_image(&mut img)
718 /// # .image_type(vk::IMAGE_TYPE_2D)
719 /// # .size(123, 123, 1)
720 /// # .usage(vk::IMAGE_USAGE_SAMPLED_BIT)
721 /// # .bind(&mut allocator, vkmem::BindType::Scatter)
722 /// # .unwrap();
723 /// //... create, bind, use ...
724 /// allocator.destroy_many(&[buf, img]);
725 /// # }
726 /// ```
727 pub fn destroy_many(&mut self, handles: &[u64]) {
728 let by_memtype = handles
729 .iter()
730 .filter_map(|h| self.handles.get(h).map(|mt| (h, mt.get())))
731 .fold(HashMap::new(), |mut acc, (h, mt)| {
732 acc.entry(mt).or_insert(Vec::new()).push(*h);
733 acc
734 });
735
736 for (mt, hs) in by_memtype.iter() {
737 self.pagetbls.get_mut(mt).unwrap().unbind(hs);
738 }
739
740 for h in handles.iter() {
741 match self.handles.remove(h) {
742 Some(Handle::Buffer(_)) => vk::DestroyBuffer(self.device, *h, std::ptr::null()),
743 Some(Handle::Image(_)) => vk::DestroyImage(self.device, *h, std::ptr::null()),
744 None => (),
745 };
746 }
747 }
748
749 /// Frees memory of unused pages
750 pub fn free_unused(&mut self) {
751 for (_, tbl) in self.pagetbls.iter_mut() {
752 tbl.free_unused();
753 }
754 }
755
756 /// Gets the physical device handle
757 pub fn get_physical_device(&self) -> vk::PhysicalDevice {
758 self.sizes.pdevice
759 }
760
761 /// Gets the device handle
762 pub fn get_device(&self) -> vk::Device {
763 self.device
764 }
765
766 fn get_mem(&self, handle: u64) -> Option<Block> {
767 self
768 .handles
769 .get(&handle)
770 .and_then(|t| self.pagetbls.get(&t.get()))
771 .and_then(|tbl| tbl.get_mem(handle))
772 }
773
774 /// Gets a [Mapped](mapped/struct.Mapped.html) of the spicified resource handle
775 ///
776 /// # Example
777 /// Creates a uniform buffer, stores some values in it and reads them back
778 ///```rust
779 /// # extern crate nobs_vk as vk;
780 /// # extern crate nobs_vkmem as vkmem;
781 /// # fn main() {
782 /// # let lib = vk::VkLib::new();
783 /// # let inst = vk::instance::new()
784 /// # .validate(vk::DEBUG_REPORT_ERROR_BIT_EXT | vk::DEBUG_REPORT_WARNING_BIT_EXT)
785 /// # .application("awesome app", 0)
786 /// # .create(lib)
787 /// # .unwrap();
788 /// # let (pdevice, device) = vk::device::PhysicalDevice::enumerate_all(inst.handle)
789 /// # .remove(0)
790 /// # .into_device()
791 /// # .add_queue(vk::device::QueueProperties {
792 /// # present: false,
793 /// # graphics: true,
794 /// # compute: true,
795 /// # transfer: true,
796 /// # }).create()
797 /// # .unwrap();
798 /// let mut allocator = vkmem::Allocator::new(pdevice.handle, device.handle);
799 ///
800 /// #[derive(Debug)]
801 /// struct Ub { a: u32, b: u32, c: u32 }
802 /// let mut buf_ub = vk::NULL_HANDLE;
803 ///
804 /// vkmem::Buffer::new(&mut buf_ub)
805 /// .size(std::mem::size_of::<Ub>() as vk::DeviceSize)
806 /// .usage(vk::BUFFER_USAGE_TRANSFER_DST_BIT | vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT)
807 /// .devicelocal(false)
808 /// .bind(&mut allocator, vkmem::BindType::Scatter)
809 /// .unwrap();
810 ///
811 /// {
812 /// let mapped = allocator.get_mapped(buf_ub).unwrap();
813 /// let ubb = Ub { a: 123, b: 4, c: 5 };
814 /// mapped.host_to_device(&ubb);
815 /// }
816 /// {
817 /// let mapped = allocator.get_mapped(buf_ub).unwrap();
818 /// let mut ubb = Ub { a: 0, b: 0, c: 0 };
819 /// mapped.device_to_host(&mut ubb);
820 /// assert_eq!(ubb.a, 123);
821 /// assert_eq!(ubb.b, 4);
822 /// assert_eq!(ubb.c, 5);
823 /// }
824 /// # }
825 /// ```
826 pub fn get_mapped(&self, handle: u64) -> Option<Mapped> {
827 self.get_mem(handle).and_then(|b| Mapped::new(self.device, b).ok())
828 }
829
830 /// Gets a [Mapped](mapped/struct.Mapped.html) of the spicified resource handle with an offset and size in bytes
831 ///
832 /// # Example
833 /// Creates a `u32` shader storage buffer, mappes it with offset and writes and reads values
834 ///
835 ///```rust
836 /// # extern crate nobs_vk as vk;
837 /// # extern crate nobs_vkmem as vkmem;
838 /// # fn main() {
839 /// # let lib = vk::VkLib::new();
840 /// # let inst = vk::instance::new()
841 /// # .validate(vk::DEBUG_REPORT_ERROR_BIT_EXT | vk::DEBUG_REPORT_WARNING_BIT_EXT)
842 /// # .application("awesome app", 0)
843 /// # .create(lib)
844 /// # .unwrap();
845 /// # let (pdevice, device) = vk::device::PhysicalDevice::enumerate_all(inst.handle)
846 /// # .remove(0)
847 /// # .into_device()
848 /// # .add_queue(vk::device::QueueProperties {
849 /// # present: false,
850 /// # graphics: true,
851 /// # compute: true,
852 /// # transfer: true,
853 /// # }).create()
854 /// # .unwrap();
855 /// let mut allocator = vkmem::Allocator::new(pdevice.handle, device.handle);
856 ///
857 /// let mut buf = vk::NULL_HANDLE;
858 /// vkmem::Buffer::new(&mut buf)
859 /// .size(123 * std::mem::size_of::<u32>() as vk::DeviceSize)
860 /// .usage(vk::BUFFER_USAGE_TRANSFER_DST_BIT | vk::BUFFER_USAGE_STORAGE_BUFFER_BIT)
861 /// .devicelocal(false)
862 /// .bind(&mut allocator, vkmem::BindType::Scatter)
863 /// .unwrap();
864 ///
865 /// {
866 /// let mut mapped = allocator.get_mapped_region(buf, 4, 100).unwrap();
867 /// let v = mapped.as_slice_mut::<u32>();
868 /// v[0] = 123;
869 /// v[1] = 4;
870 /// v[2] = 5;
871 /// }
872 /// {
873 /// let mapped = allocator.get_mapped_region(buf, 4, 100).unwrap();
874 /// let v = mapped.as_slice::<u32>();
875 /// assert_eq!(v[0], 123);
876 /// assert_eq!(v[1], 4);
877 /// assert_eq!(v[2], 5);
878 /// }
879 /// # }
880 /// ```
881 pub fn get_mapped_region(&self, handle: u64, offset: vk::DeviceSize, size: vk::DeviceSize) -> Option<Mapped> {
882 self.get_mem(handle).and_then(|b| {
883 let region = Block::new(b.begin + offset, b.begin + offset + size, b.mem);
884 match region.begin < b.end && region.end <= b.end {
885 true => Mapped::new(self.device, region).ok(),
886 false => None,
887 }
888 })
889 }
890
891 /// Print staticstics for the Allocator in yaml format
892 pub fn print_stats(&self) -> String {
893 let mut s = String::new();
894
895 let mut keys: Vec<_> = self.pagetbls.keys().collect();
896 keys.sort();
897 for k in keys {
898 write!(s, "{}", self.pagetbls[k].print_stats()).unwrap();
899 }
900 s
901 }
902}