gfx_memory/heaps/
mod.rs

1mod heap;
2mod memory_type;
3
4use self::{
5    heap::MemoryHeap,
6    memory_type::{BlockFlavor, MemoryType},
7};
8use crate::{
9    allocator::*, block::Block, mapping::MappedRange, stats::TotalMemoryUtilization,
10    usage::MemoryUsage, Size,
11};
12
13/// Possible errors returned by `Heaps`.
14#[derive(Clone, Debug, PartialEq)]
15pub enum HeapsError {
16    /// Memory allocation failure.
17    AllocationError(hal::device::AllocationError),
18    /// No memory types among required for resource were found.
19    NoSuitableMemory {
20        /// Mask of the allowed memory types.
21        mask: u32,
22        /// Requested properties.
23        properties: hal::memory::Properties,
24    },
25}
26
27impl std::fmt::Display for HeapsError {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            HeapsError::AllocationError(e) => write!(f, "{:?}", e),
31            HeapsError::NoSuitableMemory { mask, properties } => write!(
32                f,
33                "Memory type among ({}) with properties ({:?}) not found",
34                mask, properties
35            ),
36        }
37    }
38}
39impl std::error::Error for HeapsError {
40    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
41        match *self {
42            HeapsError::AllocationError(ref err) => Some(err),
43            HeapsError::NoSuitableMemory { .. } => None,
44        }
45    }
46}
47
48impl From<hal::device::AllocationError> for HeapsError {
49    fn from(error: hal::device::AllocationError) -> Self {
50        HeapsError::AllocationError(error)
51    }
52}
53
54impl From<hal::device::OutOfMemory> for HeapsError {
55    fn from(error: hal::device::OutOfMemory) -> Self {
56        HeapsError::AllocationError(error.into())
57    }
58}
59
60/// Heaps available on particular physical device.
61#[derive(Debug)]
62pub struct Heaps<B: hal::Backend> {
63    types: Vec<MemoryType<B>>,
64    heaps: Vec<MemoryHeap>,
65}
66
67impl<B: hal::Backend> Heaps<B> {
68    /// Initialize the new `Heaps` object.
69    ///
70    /// # Safety
71    /// All later operations assume the device is not lost.
72    pub unsafe fn new(
73        hal_memory_properties: &hal::adapter::MemoryProperties,
74        config_general: GeneralConfig,
75        config_linear: LinearConfig,
76        non_coherent_atom_size: Size,
77    ) -> Self {
78        Heaps {
79            types: hal_memory_properties
80                .memory_types
81                .iter()
82                .enumerate()
83                .map(|(index, mt)| {
84                    assert!(mt.heap_index < hal_memory_properties.memory_heaps.len());
85                    MemoryType::new(
86                        hal::MemoryTypeId(index),
87                        mt,
88                        config_general,
89                        config_linear,
90                        non_coherent_atom_size,
91                    )
92                })
93                .collect(),
94            heaps: hal_memory_properties
95                .memory_heaps
96                .iter()
97                .map(|&size| MemoryHeap::new(size))
98                .collect(),
99        }
100    }
101
102    /// Allocate memory block give the `requirements` from gfx-hal.
103    /// for intended `usage`, using the `kind` of allocator.
104    pub fn allocate(
105        &mut self,
106        device: &B::Device,
107        requirements: &hal::memory::Requirements,
108        usage: MemoryUsage,
109        kind: Kind,
110    ) -> Result<MemoryBlock<B>, HeapsError> {
111        let (memory_index, _, _) = {
112            let suitable_types = self
113                .types
114                .iter()
115                .enumerate()
116                .filter(|(index, _)| (requirements.type_mask & (1u32 << index)) != 0)
117                .filter_map(|(index, mt)| {
118                    if mt.properties().contains(usage.properties_required()) {
119                        let fitness = usage.memory_fitness(mt.properties());
120                        Some((index, mt, fitness))
121                    } else {
122                        None
123                    }
124                });
125
126            if suitable_types.clone().next().is_none() {
127                return Err(HeapsError::NoSuitableMemory {
128                    mask: requirements.type_mask,
129                    properties: usage.properties_required(),
130                });
131            }
132
133            suitable_types
134                .filter(|(_, mt, _)| {
135                    self.heaps[mt.heap_index()].available()
136                        > requirements.size + requirements.alignment
137                })
138                .max_by_key(|&(_, _, fitness)| fitness)
139                .ok_or_else(|| {
140                    log::error!("All suitable heaps are exhausted. {:#?}", self);
141                    hal::device::OutOfMemory::Device
142                })?
143        };
144
145        self.allocate_from(
146            device,
147            memory_index as u32,
148            kind,
149            requirements.size,
150            requirements.alignment,
151        )
152    }
153
154    /// Allocate memory block
155    /// from `memory_index` specified,
156    /// for intended `usage`,
157    /// with `size`
158    /// and `align` requirements.
159    fn allocate_from(
160        &mut self,
161        device: &B::Device,
162        memory_index: u32,
163        kind: Kind,
164        size: Size,
165        align: Size,
166    ) -> Result<MemoryBlock<B>, HeapsError> {
167        log::trace!(
168            "Allocate memory block: type '{}', kind  '{:?}', size: '{}', align: '{}'",
169            memory_index,
170            kind,
171            size,
172            align
173        );
174
175        let memory_type = &mut self.types[memory_index as usize];
176        let memory_heap = &mut self.heaps[memory_type.heap_index()];
177
178        if memory_heap.available() < size {
179            return Err(hal::device::OutOfMemory::Device.into());
180        }
181
182        let (flavor, allocated) = match memory_type.alloc(device, kind, size, align) {
183            Ok(mapping) => mapping,
184            Err(e) if kind == Kind::Linear => {
185                log::warn!("Unable to allocate {:?} with {:?}: {:?}", size, kind, e);
186                memory_type.alloc(device, Kind::Dedicated, size, align)?
187            }
188            Err(e) => return Err(e.into()),
189        };
190        memory_heap.allocated(allocated, flavor.size());
191
192        Ok(MemoryBlock {
193            flavor,
194            memory_index,
195        })
196    }
197
198    /// Free memory block.
199    ///
200    /// Memory block must be allocated from this heap.
201    pub fn free(&mut self, device: &B::Device, block: MemoryBlock<B>) {
202        let memory_index = block.memory_index;
203        let size = block.flavor.size();
204        log::trace!(
205            "Free memory block: type '{}', size: '{}'",
206            memory_index,
207            size,
208        );
209
210        let memory_type = &mut self.types[memory_index as usize];
211        let memory_heap = &mut self.heaps[memory_type.heap_index()];
212        let freed = memory_type.free(device, block.flavor);
213        memory_heap.freed(freed, size);
214    }
215
216    /// Clear allocators.
217    /// Call this before dropping an instance of [`Heaps`]
218    /// or if you are low on memory.
219    ///
220    /// Internally calls the clear methods on all
221    /// internal [`LinearAllocator`] and [`GeneralAllocator`] instances.
222    pub fn clear(&mut self, device: &B::Device) {
223        for memory_type in self.types.iter_mut() {
224            let memory_heap = &mut self.heaps[memory_type.heap_index()];
225            let freed = memory_type.clear(device);
226            memory_heap.freed(freed, 0);
227        }
228    }
229
230    /// Get memory utilization.
231    pub fn utilization(&self) -> TotalMemoryUtilization {
232        TotalMemoryUtilization {
233            heaps: self.heaps.iter().map(MemoryHeap::utilization).collect(),
234            types: self.types.iter().map(MemoryType::utilization).collect(),
235        }
236    }
237}
238
239impl<B: hal::Backend> Drop for Heaps<B> {
240    fn drop(&mut self) {
241        for memory_heap in &self.heaps {
242            let utilization = memory_heap.utilization();
243            if utilization.utilization.used != 0 || utilization.utilization.effective != 0 {
244                log::error!(
245                    "Heaps not completely freed before drop. Utilization: {:?}",
246                    utilization
247                );
248            }
249        }
250    }
251}
252
253/// Memory block allocated from `Heaps`.
254#[derive(Debug)]
255pub struct MemoryBlock<B: hal::Backend> {
256    flavor: BlockFlavor<B>,
257    memory_index: u32,
258}
259
260impl<B: hal::Backend> MemoryBlock<B> {
261    /// Get memory type id.
262    pub fn memory_type(&self) -> u32 {
263        self.memory_index
264    }
265}
266
267impl<B: hal::Backend> Block<B> for MemoryBlock<B> {
268    fn properties(&self) -> hal::memory::Properties {
269        match self.flavor {
270            BlockFlavor::Dedicated(ref block) => block.properties(),
271            BlockFlavor::General(ref block) => block.properties(),
272            BlockFlavor::Linear(ref block) => block.properties(),
273        }
274    }
275
276    fn memory(&self) -> &B::Memory {
277        match self.flavor {
278            BlockFlavor::Dedicated(ref block) => block.memory(),
279            BlockFlavor::General(ref block) => block.memory(),
280            BlockFlavor::Linear(ref block) => block.memory(),
281        }
282    }
283
284    fn segment(&self) -> hal::memory::Segment {
285        match self.flavor {
286            BlockFlavor::Dedicated(ref block) => block.segment(),
287            BlockFlavor::General(ref block) => block.segment(),
288            BlockFlavor::Linear(ref block) => block.segment(),
289        }
290    }
291
292    fn map<'a>(
293        &'a mut self,
294        device: &B::Device,
295        segment: hal::memory::Segment,
296    ) -> Result<MappedRange<'a, B>, hal::device::MapError> {
297        match self.flavor {
298            BlockFlavor::Dedicated(ref mut block) => block.map(device, segment),
299            BlockFlavor::General(ref mut block) => block.map(device, segment),
300            BlockFlavor::Linear(ref mut block) => block.map(device, segment),
301        }
302    }
303}