ohm 0.0.0

High performance 2D graphics library
Documentation
//! 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,
        })
}