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#[derive(Clone, Debug, PartialEq)]
15pub enum HeapsError {
16 AllocationError(hal::device::AllocationError),
18 NoSuitableMemory {
20 mask: u32,
22 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#[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 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 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 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 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 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 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#[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 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}