rendy_memory/heaps/
mod.rs

1mod heap;
2mod memory_type;
3
4use {
5    self::{heap::MemoryHeap, memory_type::MemoryType},
6    crate::{allocator::*, block::Block, mapping::*, usage::MemoryUsage, util::*, utilization::*},
7    std::ops::Range,
8};
9
10/// Possible errors returned by `Heaps`.
11#[allow(missing_copy_implementations)]
12#[derive(Clone, Debug, PartialEq)]
13pub enum HeapsError {
14    /// Memory allocation failure.
15    AllocationError(gfx_hal::device::AllocationError),
16    /// No memory types among required for resource with requested properties was found.
17    NoSuitableMemory(u32, gfx_hal::memory::Properties),
18}
19
20impl std::fmt::Display for HeapsError {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            HeapsError::AllocationError(e) => write!(f, "{:?}", e),
24            HeapsError::NoSuitableMemory(e, e2) => write!(
25                f,
26                "Memory type among ({}) with properties ({:?}) not found",
27                e, e2
28            ),
29        }
30    }
31}
32impl std::error::Error for HeapsError {}
33
34impl From<gfx_hal::device::AllocationError> for HeapsError {
35    fn from(error: gfx_hal::device::AllocationError) -> Self {
36        HeapsError::AllocationError(error)
37    }
38}
39
40impl From<gfx_hal::device::OutOfMemory> for HeapsError {
41    fn from(error: gfx_hal::device::OutOfMemory) -> Self {
42        HeapsError::AllocationError(error.into())
43    }
44}
45
46/// Config for `Heaps` allocator.
47#[derive(Clone, Copy, Debug)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct HeapsConfig {
50    /// Config for linear sub-allocator.
51    pub linear: Option<LinearConfig>,
52
53    /// Config for dynamic sub-allocator.
54    pub dynamic: Option<DynamicConfig>,
55}
56
57/// Heaps available on particular physical device.
58#[derive(Debug)]
59pub struct Heaps<B: gfx_hal::Backend> {
60    types: Vec<MemoryType<B>>,
61    heaps: Vec<MemoryHeap>,
62}
63
64impl<B> Heaps<B>
65where
66    B: gfx_hal::Backend,
67{
68    /// This must be called with `gfx_hal::memory::Properties` fetched from physical device.
69    pub unsafe fn new<P, H>(types: P, heaps: H) -> Self
70    where
71        P: IntoIterator<Item = (gfx_hal::memory::Properties, u32, HeapsConfig)>,
72        H: IntoIterator<Item = u64>,
73    {
74        let heaps = heaps
75            .into_iter()
76            .map(|size| MemoryHeap::new(size))
77            .collect::<Vec<_>>();
78        Heaps {
79            types: types
80                .into_iter()
81                .enumerate()
82                .map(|(index, (properties, heap_index, config))| {
83                    assert!(
84                        fits_u32(index),
85                        "Number of memory types must fit in u32 limit"
86                    );
87                    assert!(
88                        fits_usize(heap_index),
89                        "Number of memory types must fit in u32 limit"
90                    );
91                    let memory_type = gfx_hal::MemoryTypeId(index);
92                    let heap_index = heap_index as usize;
93                    assert!(heap_index < heaps.len());
94                    MemoryType::new(memory_type, heap_index, properties, config)
95                })
96                .collect(),
97            heaps,
98        }
99    }
100
101    /// Allocate memory block
102    /// from one of memory types specified by `mask`,
103    /// for intended `usage`,
104    /// with `size`
105    /// and `align` requirements.
106    pub fn allocate(
107        &mut self,
108        device: &B::Device,
109        mask: u32,
110        usage: impl MemoryUsage,
111        size: u64,
112        align: u64,
113    ) -> Result<MemoryBlock<B>, HeapsError> {
114        debug_assert!(fits_u32(self.types.len()));
115
116        let (memory_index, _, _) = {
117            let suitable_types = self
118                .types
119                .iter()
120                .enumerate()
121                .filter(|(index, _)| (mask & (1u32 << index)) != 0)
122                .filter_map(|(index, mt)| {
123                    if mt.properties().contains(usage.properties_required()) {
124                        let fitness = usage.memory_fitness(mt.properties());
125                        Some((index, mt, fitness))
126                    } else {
127                        None
128                    }
129                })
130                .collect::<smallvec::SmallVec<[_; 64]>>();
131
132            if suitable_types.is_empty() {
133                return Err(HeapsError::NoSuitableMemory(
134                    mask,
135                    usage.properties_required(),
136                ));
137            }
138
139            suitable_types
140                .into_iter()
141                .filter(|(_, mt, _)| self.heaps[mt.heap_index()].available() > size + align)
142                .max_by_key(|&(_, _, fitness)| fitness)
143                .ok_or_else(|| {
144                    log::error!("All suitable heaps are exhausted. {:#?}", self);
145                    gfx_hal::device::OutOfMemory::Device
146                })?
147        };
148
149        self.allocate_from(device, memory_index as u32, usage, size, align)
150    }
151
152    /// Allocate memory block
153    /// from `memory_index` specified,
154    /// for intended `usage`,
155    /// with `size`
156    /// and `align` requirements.
157    fn allocate_from(
158        &mut self,
159        device: &B::Device,
160        memory_index: u32,
161        usage: impl MemoryUsage,
162        size: u64,
163        align: u64,
164    ) -> Result<MemoryBlock<B>, HeapsError> {
165        log::trace!(
166            "Allocate memory block: type '{}', usage '{:#?}', size: '{}', align: '{}'",
167            memory_index,
168            usage,
169            size,
170            align
171        );
172        assert!(fits_usize(memory_index));
173
174        let ref mut memory_type = self.types[memory_index as usize];
175        let ref mut memory_heap = self.heaps[memory_type.heap_index()];
176
177        if memory_heap.available() < size {
178            return Err(gfx_hal::device::OutOfMemory::Device.into());
179        }
180
181        let (block, allocated) = memory_type.alloc(device, usage, size, align)?;
182        memory_heap.allocated(allocated, block.size());
183
184        Ok(MemoryBlock {
185            block,
186            memory_index,
187        })
188    }
189
190    /// Free memory block.
191    ///
192    /// Memory block must be allocated from this heap.
193    pub fn free(&mut self, device: &B::Device, block: MemoryBlock<B>) {
194        // trace!("Free block '{:#?}'", block);
195        let memory_index = block.memory_index;
196        debug_assert!(fits_usize(memory_index));
197        let size = block.size();
198
199        let ref mut memory_type = self.types[memory_index as usize];
200        let ref mut memory_heap = self.heaps[memory_type.heap_index()];
201        let freed = memory_type.free(device, block.block);
202        memory_heap.freed(freed, size);
203    }
204
205    /// Dispose of allocator.
206    /// Cleanup allocators before dropping.
207    /// Will panic if memory instances are left allocated.
208    pub fn dispose(self, device: &B::Device) {
209        for mt in self.types {
210            mt.dispose(device)
211        }
212    }
213
214    /// Get memory utilization.
215    pub fn utilization(&self) -> TotalMemoryUtilization {
216        TotalMemoryUtilization {
217            heaps: self.heaps.iter().map(MemoryHeap::utilization).collect(),
218            types: self.types.iter().map(MemoryType::utilization).collect(),
219        }
220    }
221}
222
223/// Memory block allocated from `Heaps`.
224#[derive(Debug)]
225pub struct MemoryBlock<B: gfx_hal::Backend> {
226    block: BlockFlavor<B>,
227    memory_index: u32,
228}
229
230impl<B> MemoryBlock<B>
231where
232    B: gfx_hal::Backend,
233{
234    /// Get memory type id.
235    pub fn memory_type(&self) -> u32 {
236        self.memory_index
237    }
238}
239
240#[derive(Debug)]
241enum BlockFlavor<B: gfx_hal::Backend> {
242    Dedicated(DedicatedBlock<B>),
243    Linear(LinearBlock<B>),
244    Dynamic(DynamicBlock<B>),
245    // Chunk(ChunkBlock<B>),
246}
247
248macro_rules! any_block {
249    ($self:ident. $block:ident => $expr:expr) => {{
250        use self::BlockFlavor::*;
251        match $self.$block {
252            Dedicated($block) => $expr,
253            Linear($block) => $expr,
254            Dynamic($block) => $expr,
255            // Chunk($block) => $expr,
256        }
257    }};
258    (& $self:ident. $block:ident => $expr:expr) => {{
259        use self::BlockFlavor::*;
260        match &$self.$block {
261            Dedicated($block) => $expr,
262            Linear($block) => $expr,
263            Dynamic($block) => $expr,
264            // Chunk($block) => $expr,
265        }
266    }};
267    (&mut $self:ident. $block:ident => $expr:expr) => {{
268        use self::BlockFlavor::*;
269        match &mut $self.$block {
270            Dedicated($block) => $expr,
271            Linear($block) => $expr,
272            Dynamic($block) => $expr,
273            // Chunk($block) => $expr,
274        }
275    }};
276}
277
278impl<B> BlockFlavor<B>
279where
280    B: gfx_hal::Backend,
281{
282    #[inline]
283    fn size(&self) -> u64 {
284        use self::BlockFlavor::*;
285        match self {
286            Dedicated(block) => block.size(),
287            Linear(block) => block.size(),
288            Dynamic(block) => block.size(),
289            // Chunk(block) => block.size(),
290        }
291    }
292}
293
294impl<B> Block<B> for MemoryBlock<B>
295where
296    B: gfx_hal::Backend,
297{
298    #[inline]
299    fn properties(&self) -> gfx_hal::memory::Properties {
300        any_block!(&self.block => block.properties())
301    }
302
303    #[inline]
304    fn memory(&self) -> &B::Memory {
305        any_block!(&self.block => block.memory())
306    }
307
308    #[inline]
309    fn range(&self) -> Range<u64> {
310        any_block!(&self.block => block.range())
311    }
312
313    fn map<'a>(
314        &'a mut self,
315        device: &B::Device,
316        range: Range<u64>,
317    ) -> Result<MappedRange<'a, B>, gfx_hal::device::MapError> {
318        any_block!(&mut self.block => block.map(device, range))
319    }
320
321    fn unmap(&mut self, device: &B::Device) {
322        any_block!(&mut self.block => block.unmap(device))
323    }
324}