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}