1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! This module defines traits and implementations for managing device (gpu)
//! memory.
//!
//! Big Idea: Allocators should compose.
//!
//! Allocator Implementations (brainstorm):
//! - passthrough allocator -> directly allocates a new block of device memory
//!   - done!
//! - metric-gathering allocator -> decorates an allocator with metrics
//!   - done!
//! - pooling allocator -> something something, gpu memory pools

mod allocation;
mod forced_offset;
mod mem_unit;
mod metrics;
mod page_allocator;
mod passthrough;
mod pool;
mod shared_ref;
mod size_selector;
mod suballocator;
mod type_index;

#[cfg(test)]
mod stub_allocator;

use anyhow::Result;
use ash::vk;

pub use self::{
    forced_offset::ForcedOffsetAllocator,
    mem_unit::MemUnit,
    metrics::{ConsoleMarkdownReport, MetricsAllocator},
    page_allocator::PageAllocator,
    passthrough::PassthroughAllocator,
    pool::PoolAllocator,
    shared_ref::SharedRefAllocator,
    size_selector::SizeSelector,
    suballocator::Suballocator,
    type_index::TypeIndexAllocator,
};

/// A single allocated piece of device memory.
#[derive(Clone, Debug)]
pub struct Allocation {
    pub memory: vk::DeviceMemory,
    pub offset: vk::DeviceSize,
    pub byte_size: vk::DeviceSize,
    memory_type_index: u32,
}

/// The external device memory allocation interface. This is the api used by
/// applications to allocate and free memory on the gpu.
pub trait DeviceAllocator {
    /// Allocate device memory with the provided type index and size.
    ///
    /// # unsafe because
    ///
    /// - it is the responsibility of the caller to free the returned memory
    ///   when it is no longer in use
    /// - implementations do not generally check that the memory type index in
    ///   allocate_info is the correct memory type index, the arguments are
    ///   assumed to be correct
    unsafe fn allocate(
        &mut self,
        allocate_info: vk::MemoryAllocateInfo,
    ) -> Result<Allocation>;

    /// Free an allocated piece of device memory.
    ///
    /// # unsafe because
    ///
    /// - it is the responsibility of the caller to know when the GPU is no
    ///   longer using the allocation
    unsafe fn free(&mut self, allocation: &Allocation) -> Result<()>;
}

/// Build the standard allocator implementation.
///
/// The return is a boxed impl so that consumers are not dependent on the
/// specific implementation (often the full type is unwieldy because it is a
/// composition of DeviceAllocator implementations).
///
/// The caller is responsible for keeping the ash instance, logical device, and
/// physical device alive for at least as long as the allocator exists.
pub fn build_standard_allocator(
    ash_instance: ash::Instance,
    logical_device: ash::Device,
    physical_device: ash::vk::PhysicalDevice,
) -> Box<impl DeviceAllocator> {
    let device_allocator = SharedRefAllocator::new(MetricsAllocator::new(
        "Device Allocator",
        ConsoleMarkdownReport::new(ash_instance.clone(), physical_device),
        PassthroughAllocator::create(logical_device),
    ));

    let typed_allocator = PageAllocator::new(
        TypeIndexAllocator::new(
            &ash_instance,
            physical_device,
            |_memory_type_index, _memory_type| {
                SizeSelector::new(
                    // For allocations below 512KiB
                    PoolAllocator::new(
                        device_allocator.clone(),
                        MemUnit::MiB(1),
                    ),
                    MemUnit::KiB(512),
                    // for allocations above 512KiB
                    SizeSelector::new(
                        // for allocations below 256MiB
                        PoolAllocator::new(
                            device_allocator.clone(),
                            MemUnit::MiB(512),
                        ),
                        MemUnit::MiB(256),
                        // for allocations above 256MiB
                        device_allocator.clone(),
                    ),
                )
            },
        ),
        MemUnit::KiB(1),
    );

    Box::new(typed_allocator)
}