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
129
130
131
132
133
//! Memory allocator interface

pub mod chunk;
pub mod hybrid;
pub mod passthrough;

pub use self::hybrid::{HybridAllocation, HybridAllocator};

use std::ops::Range;

use gfx_hal::adapter::{MemoryProperties, MemoryTypeId};
use gfx_hal::device::OutOfMemory;
use gfx_hal::memory::{Properties, Requirements};
use gfx_hal::Backend;

use failure::Fail;

/// Error that can occur during memory allocation.
#[derive(Debug, Fail)]
pub enum AllocationError {
    /// Either out of host memory, or out of device (GPU) memory.
    #[fail(display = "{}", _0)]
    OutOfMemory(OutOfMemory),

    /// Too many memory chunks allocated. Some implementations might limit the max number of allocations.
    #[fail(display = "Too many objects allocated")]
    TooManyObjects,

    /// The device doesn't support memory with requested properties.
    #[fail(
        display = "No suitable memory type found among {:#b} with flags {:?}",
        type_mask, flags
    )]
    NoSuitableHeap { type_mask: u64, flags: Properties },
}

impl From<gfx_hal::device::AllocationError> for AllocationError {
    fn from(v: gfx_hal::device::AllocationError) -> AllocationError {
        match v {
            gfx_hal::device::AllocationError::OutOfMemory(o) => AllocationError::OutOfMemory(o),
            gfx_hal::device::AllocationError::TooManyObjects => AllocationError::TooManyObjects,
        }
    }
}

/// Allocator hint to distribute chunks in a more optimal way.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Hint {
    /// Lifetime of the object is big
    Permanent,

    /// Object is short-term, perhaps a temporary buffer, which will be deleted in a moment after creation
    ShortTerm,
}

/// Specification of a new allocation
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AllocationSpec {
    pub hint: Hint,

    /// Required memory properties
    pub required_flags: Properties,

    /// Preferred memory properties. The allocator will likely choose a heap with preferred flags present.
    pub preferred_flags: Properties,
}

impl AllocationSpec {
    pub fn new(
        hint: Hint,
        required_flags: Properties,
        preferred_flags: Properties,
    ) -> AllocationSpec {
        AllocationSpec {
            hint,
            required_flags,
            preferred_flags,
        }
    }
}

/// Trait implemented by memory chunks returned from the allocator.
pub trait Allocation<B: Backend> {
    /// Memory associated with the chunk
    fn memory(&self) -> &B::Memory;

    /// Flags of the associated memory
    fn flags(&self) -> Properties;

    /// Range of the chunk in the memory
    fn range(&self) -> Range<u64>;
}

/// Memory allocator
pub trait Allocator<B: Backend> {
    type Allocation: Allocation<B>;

    /// Allocate a new chunk on the device with specified requirements and specification.
    ///
    /// Allocated chunk __must__ be freed afterwards.
    fn alloc(
        &mut self,
        device: &B::Device,
        req: Requirements,
        spec: AllocationSpec,
    ) -> Result<Self::Allocation, AllocationError>;

    /// Free a previously allocated chunk.
    fn free(&mut self, device: &B::Device, allocation: Self::Allocation);
}

/// Pick a memory type based on specification.
pub(crate) fn pick_memory_type(
    props: &MemoryProperties,
    req: Requirements,
    spec: AllocationSpec,
) -> Result<MemoryTypeId, AllocationError> {
    props
        .memory_types
        .iter()
        .enumerate()
        .filter(|&(i, ty)| {
            req.size <= props.memory_heaps[ty.heap_index]
                && req.type_mask & (1 << i as u64) != 0
                && spec.required_flags.contains(ty.properties)
        })
        .max_by_key(|(_, ty)| (ty.properties & spec.preferred_flags).bits().count_ones())
        .map(|(i, _)| MemoryTypeId(i))
        .ok_or(AllocationError::NoSuitableHeap {
            type_mask: req.type_mask,
            flags: spec.required_flags,
        })
}