1#[cfg(feature = "std")]
2use alloc::sync::Arc;
3use alloc::{boxed::Box, string::String, vec::Vec};
4use core::{
5 fmt,
6 mem::size_of_val,
8};
9#[cfg(feature = "std")]
10use std::backtrace::Backtrace;
11
12use log::{debug, warn, Level};
13use windows::Win32::{
14 Foundation::E_OUTOFMEMORY,
15 Graphics::{
16 Direct3D12::*,
17 Dxgi::{Common::DXGI_FORMAT, DXGI_ERROR_DEVICE_REMOVED},
18 },
19};
20
21#[cfg(feature = "visualizer")]
22mod visualizer;
23#[cfg(feature = "visualizer")]
24pub use visualizer::AllocatorVisualizer;
25
26use crate::{
27 allocator::{
28 AllocationType, AllocatorReport, DedicatedBlockAllocator, FreeListAllocator,
29 MemoryBlockReport, SubAllocator,
30 },
31 AllocationError, AllocationSizes, AllocatorDebugSettings, MemoryLocation, Result,
32};
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub enum ResourceCategory {
38 Buffer,
39 RtvDsvTexture,
40 OtherTexture,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum ResourceStateOrBarrierLayout {
45 ResourceState(D3D12_RESOURCE_STATES),
46 BarrierLayout(D3D12_BARRIER_LAYOUT),
47}
48
49#[derive(Clone, Copy)]
50pub struct ResourceCreateDesc<'a> {
51 pub name: &'a str,
52 pub memory_location: MemoryLocation,
53 pub resource_category: ResourceCategory,
54 pub resource_desc: &'a D3D12_RESOURCE_DESC,
55 pub castable_formats: &'a [DXGI_FORMAT],
56 pub clear_value: Option<&'a D3D12_CLEAR_VALUE>,
57 pub initial_state_or_layout: ResourceStateOrBarrierLayout,
58 pub resource_type: &'a ResourceType<'a>,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62enum HeapCategory {
63 All,
64 Buffer,
65 RtvDsvTexture,
66 OtherTexture,
67}
68
69impl From<ResourceCategory> for HeapCategory {
70 fn from(resource_category: ResourceCategory) -> Self {
71 match resource_category {
72 ResourceCategory::Buffer => Self::Buffer,
73 ResourceCategory::RtvDsvTexture => Self::RtvDsvTexture,
74 ResourceCategory::OtherTexture => Self::OtherTexture,
75 }
76 }
77}
78
79impl From<&D3D12_RESOURCE_DESC> for ResourceCategory {
80 fn from(desc: &D3D12_RESOURCE_DESC) -> Self {
81 if desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER {
82 Self::Buffer
83 } else if (desc.Flags
84 & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
85 != D3D12_RESOURCE_FLAG_NONE
86 {
87 Self::RtvDsvTexture
88 } else {
89 Self::OtherTexture
90 }
91 }
92}
93
94#[derive(Clone, Debug)]
95pub struct AllocationCreateDesc<'a> {
96 pub name: &'a str,
98 pub location: MemoryLocation,
100
101 pub size: u64,
103 pub alignment: u64,
105 pub resource_category: ResourceCategory,
111}
112
113impl<'a> AllocationCreateDesc<'a> {
114 pub fn from_d3d12_resource_desc(
117 device: &ID3D12Device,
118 desc: &D3D12_RESOURCE_DESC,
119 name: &'a str,
120 location: MemoryLocation,
121 ) -> Self {
122 let allocation_info =
125 unsafe { device.GetResourceAllocationInfo(0, core::slice::from_ref(desc)) };
126 let resource_category: ResourceCategory = desc.into();
127
128 AllocationCreateDesc {
129 name,
130 location,
131 size: allocation_info.SizeInBytes,
132 alignment: allocation_info.Alignment,
133 resource_category,
134 }
135 }
136}
137
138#[derive(Clone, Debug)]
139pub enum ID3D12DeviceVersion {
140 Device(ID3D12Device),
143 Device10(ID3D12Device10),
146 Device12(ID3D12Device12),
148}
149
150impl core::ops::Deref for ID3D12DeviceVersion {
151 type Target = ID3D12Device;
152
153 fn deref(&self) -> &Self::Target {
154 match self {
155 Self::Device(device) => device,
156 Self::Device10(device10) => device10.into(),
157 Self::Device12(device12) => device12.into(),
158 }
159 }
160}
161
162#[derive(Debug)]
163pub struct AllocatorCreateDesc {
164 pub device: ID3D12DeviceVersion,
165 pub debug_settings: AllocatorDebugSettings,
166 pub allocation_sizes: AllocationSizes,
167}
168
169pub enum ResourceType<'a> {
170 Committed {
174 heap_properties: &'a D3D12_HEAP_PROPERTIES,
175 heap_flags: D3D12_HEAP_FLAGS,
176 },
177 Placed,
181}
182
183#[derive(Debug)]
184pub struct Resource {
185 name: String,
186 pub allocation: Option<Allocation>,
187 resource: Option<ID3D12Resource>,
188 pub memory_location: MemoryLocation,
189 memory_type_index: Option<usize>,
190 pub size: u64,
191}
192
193impl Resource {
194 pub fn resource(&self) -> &ID3D12Resource {
195 self.resource.as_ref().expect("Resource was already freed.")
196 }
197}
198
199impl Drop for Resource {
200 fn drop(&mut self) {
201 if self.resource.is_some() {
202 warn!("Dropping resource `{}` that was not freed. Call `Allocator::free_resource(resource)` instead.", self.name);
203 }
204 }
205}
206
207#[derive(Debug)]
208pub struct CommittedAllocationStatistics {
209 pub num_allocations: usize,
210 pub total_size: u64,
211}
212
213#[derive(Debug)]
214pub struct Allocation {
215 chunk_id: Option<core::num::NonZeroU64>,
216 offset: u64,
217 size: u64,
218 memory_block_index: usize,
219 memory_type_index: usize,
220 heap: ID3D12Heap,
221
222 name: Option<Box<str>>,
223}
224
225impl Allocation {
226 pub fn chunk_id(&self) -> Option<core::num::NonZeroU64> {
227 self.chunk_id
228 }
229
230 pub unsafe fn heap(&self) -> &ID3D12Heap {
242 &self.heap
243 }
244
245 pub fn offset(&self) -> u64 {
248 self.offset
249 }
250
251 pub fn size(&self) -> u64 {
253 self.size
254 }
255
256 pub fn is_null(&self) -> bool {
257 self.chunk_id.is_none()
258 }
259}
260
261#[derive(Debug)]
262struct MemoryBlock {
263 heap: ID3D12Heap,
264 size: u64,
265 sub_allocator: Box<dyn SubAllocator>,
266}
267impl MemoryBlock {
268 fn new(
269 device: &ID3D12Device,
270 size: u64,
271 heap_properties: &D3D12_HEAP_PROPERTIES,
272 heap_category: HeapCategory,
273 dedicated: bool,
274 ) -> Result<Self> {
275 let heap = {
276 let mut desc = D3D12_HEAP_DESC {
277 SizeInBytes: size,
278 Properties: *heap_properties,
279 Alignment: D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT as u64,
280 ..Default::default()
281 };
282 desc.Flags = match heap_category {
283 HeapCategory::All => D3D12_HEAP_FLAG_NONE,
284 HeapCategory::Buffer => D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,
285 HeapCategory::RtvDsvTexture => D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES,
286 HeapCategory::OtherTexture => D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES,
287 };
288
289 let mut heap = None;
290 let hr = unsafe { device.CreateHeap(&desc, &mut heap) };
291 match hr {
292 Err(e) if e.code() == E_OUTOFMEMORY => Err(AllocationError::OutOfMemory),
293 Err(e) => Err(AllocationError::Internal(format!(
294 "ID3D12Device::CreateHeap failed: {e}"
295 ))),
296 Ok(()) => heap.ok_or_else(|| {
297 AllocationError::Internal(
298 "ID3D12Heap pointer is null, but should not be.".into(),
299 )
300 }),
301 }?
302 };
303
304 let sub_allocator: Box<dyn SubAllocator> = if dedicated {
305 Box::new(DedicatedBlockAllocator::new(size))
306 } else {
307 Box::new(FreeListAllocator::new(size))
308 };
309
310 Ok(Self {
311 heap,
312 size,
313 sub_allocator,
314 })
315 }
316}
317
318#[derive(Debug)]
319struct MemoryType {
320 memory_blocks: Vec<Option<MemoryBlock>>,
321 committed_allocations: CommittedAllocationStatistics,
322 memory_location: MemoryLocation,
323 heap_category: HeapCategory,
324 heap_properties: D3D12_HEAP_PROPERTIES,
325 memory_type_index: usize,
326 active_general_blocks: usize,
327}
328
329impl MemoryType {
330 fn allocate(
331 &mut self,
332 device: &ID3D12DeviceVersion,
333 desc: &AllocationCreateDesc<'_>,
334 #[cfg(feature = "std")] backtrace: Arc<Backtrace>,
335 allocation_sizes: &AllocationSizes,
336 ) -> Result<Allocation> {
337 let allocation_type = AllocationType::Linear;
338
339 let is_host = self.heap_properties.Type != D3D12_HEAP_TYPE_DEFAULT;
340 let memblock_size = allocation_sizes.get_memblock_size(is_host, self.active_general_blocks);
341
342 let size = desc.size;
343 let alignment = desc.alignment;
344
345 if size > memblock_size {
347 let mem_block = MemoryBlock::new(
348 device,
349 size,
350 &self.heap_properties,
351 self.heap_category,
352 true,
353 )?;
354
355 let block_index = self.memory_blocks.iter().position(|block| block.is_none());
356 let block_index = match block_index {
357 Some(i) => {
358 self.memory_blocks[i].replace(mem_block);
359 i
360 }
361 None => {
362 self.memory_blocks.push(Some(mem_block));
363 self.memory_blocks.len() - 1
364 }
365 };
366
367 let mem_block = self.memory_blocks[block_index]
368 .as_mut()
369 .ok_or_else(|| AllocationError::Internal("Memory block must be Some".into()))?;
370
371 let (offset, chunk_id) = mem_block.sub_allocator.allocate(
372 size,
373 alignment,
374 allocation_type,
375 1,
376 desc.name,
377 #[cfg(feature = "std")]
378 backtrace,
379 )?;
380
381 return Ok(Allocation {
382 chunk_id: Some(chunk_id),
383 size,
384 offset,
385 memory_block_index: block_index,
386 memory_type_index: self.memory_type_index,
387 heap: mem_block.heap.clone(),
388 name: Some(desc.name.into()),
389 });
390 }
391
392 let mut empty_block_index = None;
393 for (mem_block_i, mem_block) in self.memory_blocks.iter_mut().enumerate().rev() {
394 if let Some(mem_block) = mem_block {
395 let allocation = mem_block.sub_allocator.allocate(
396 size,
397 alignment,
398 allocation_type,
399 1,
400 desc.name,
401 #[cfg(feature = "std")]
402 backtrace.clone(),
403 );
404
405 match allocation {
406 Ok((offset, chunk_id)) => {
407 return Ok(Allocation {
408 chunk_id: Some(chunk_id),
409 offset,
410 size,
411 memory_block_index: mem_block_i,
412 memory_type_index: self.memory_type_index,
413 heap: mem_block.heap.clone(),
414 name: Some(desc.name.into()),
415 });
416 }
417 Err(AllocationError::OutOfMemory) => {} Err(err) => return Err(err), }
420 } else if empty_block_index.is_none() {
421 empty_block_index = Some(mem_block_i);
422 }
423 }
424
425 let new_memory_block = MemoryBlock::new(
426 device,
427 memblock_size,
428 &self.heap_properties,
429 self.heap_category,
430 false,
431 )?;
432
433 let new_block_index = if let Some(block_index) = empty_block_index {
434 self.memory_blocks[block_index] = Some(new_memory_block);
435 block_index
436 } else {
437 self.memory_blocks.push(Some(new_memory_block));
438 self.memory_blocks.len() - 1
439 };
440
441 self.active_general_blocks += 1;
442
443 let mem_block = self.memory_blocks[new_block_index]
444 .as_mut()
445 .ok_or_else(|| AllocationError::Internal("Memory block must be Some".into()))?;
446 let allocation = mem_block.sub_allocator.allocate(
447 size,
448 alignment,
449 allocation_type,
450 1,
451 desc.name,
452 #[cfg(feature = "std")]
453 backtrace,
454 );
455 let (offset, chunk_id) = match allocation {
456 Err(AllocationError::OutOfMemory) => Err(AllocationError::Internal(
457 "Allocation that must succeed failed. This is a bug in the allocator.".into(),
458 )),
459 a => a,
460 }?;
461
462 Ok(Allocation {
463 chunk_id: Some(chunk_id),
464 offset,
465 size,
466 memory_block_index: new_block_index,
467 memory_type_index: self.memory_type_index,
468 heap: mem_block.heap.clone(),
469 name: Some(desc.name.into()),
470 })
471 }
472
473 #[allow(clippy::needless_pass_by_value)]
474 fn free(&mut self, allocation: Allocation) -> Result<()> {
475 let block_idx = allocation.memory_block_index;
476
477 let mem_block = self.memory_blocks[block_idx]
478 .as_mut()
479 .ok_or_else(|| AllocationError::Internal("Memory block must be Some.".into()))?;
480
481 mem_block.sub_allocator.free(allocation.chunk_id)?;
482
483 let is_dedicated_or_not_last_general_block =
487 !mem_block.sub_allocator.supports_general_allocations()
488 || self.active_general_blocks > 1;
489 if mem_block.sub_allocator.is_empty() && is_dedicated_or_not_last_general_block {
490 let block = self.memory_blocks[block_idx]
491 .take()
492 .ok_or_else(|| AllocationError::Internal("Memory block must be Some.".into()))?;
493
494 if block.sub_allocator.supports_general_allocations() {
495 self.active_general_blocks -= 1;
496 }
497
498 }
500
501 Ok(())
502 }
503}
504
505pub struct Allocator {
506 device: ID3D12DeviceVersion,
507 debug_settings: AllocatorDebugSettings,
508 memory_types: Vec<MemoryType>,
509 allocation_sizes: AllocationSizes,
510}
511
512impl Allocator {
513 pub fn device(&self) -> &ID3D12DeviceVersion {
514 &self.device
515 }
516
517 pub fn new(desc: &AllocatorCreateDesc) -> Result<Self> {
518 let device = desc.device.clone();
520
521 let mut options = Default::default();
523 unsafe {
524 device.CheckFeatureSupport(
525 D3D12_FEATURE_D3D12_OPTIONS,
526 <*mut D3D12_FEATURE_DATA_D3D12_OPTIONS>::cast(&mut options),
527 size_of_val(&options) as u32,
528 )
529 }
530 .map_err(|e| {
531 AllocationError::Internal(format!("ID3D12Device::CheckFeatureSupport failed: {e}"))
532 })?;
533
534 let is_heap_tier1 = options.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_1;
535
536 let heap_types = [
537 (
538 MemoryLocation::GpuOnly,
539 D3D12_HEAP_PROPERTIES {
540 Type: D3D12_HEAP_TYPE_DEFAULT,
541 ..Default::default()
542 },
543 ),
544 (
545 MemoryLocation::CpuToGpu,
546 D3D12_HEAP_PROPERTIES {
547 Type: D3D12_HEAP_TYPE_CUSTOM,
548 CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
549 MemoryPoolPreference: D3D12_MEMORY_POOL_L0,
550 ..Default::default()
551 },
552 ),
553 (
554 MemoryLocation::GpuToCpu,
555 D3D12_HEAP_PROPERTIES {
556 Type: D3D12_HEAP_TYPE_CUSTOM,
557 CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
558 MemoryPoolPreference: D3D12_MEMORY_POOL_L0,
559 ..Default::default()
560 },
561 ),
562 ];
563
564 let heap_types = if is_heap_tier1 {
565 heap_types
566 .iter()
567 .flat_map(|(memory_location, heap_properties)| {
568 [
569 (HeapCategory::Buffer, *memory_location, *heap_properties),
570 (
571 HeapCategory::RtvDsvTexture,
572 *memory_location,
573 *heap_properties,
574 ),
575 (
576 HeapCategory::OtherTexture,
577 *memory_location,
578 *heap_properties,
579 ),
580 ]
581 .to_vec()
582 })
583 .collect::<Vec<_>>()
584 } else {
585 heap_types
586 .iter()
587 .map(|(memory_location, heap_properties)| {
588 (HeapCategory::All, *memory_location, *heap_properties)
589 })
590 .collect::<Vec<_>>()
591 };
592
593 let memory_types = heap_types
594 .iter()
595 .enumerate()
596 .map(
597 |(i, &(heap_category, memory_location, heap_properties))| MemoryType {
598 memory_blocks: Vec::default(),
599 memory_location,
600 heap_category,
601 heap_properties,
602 memory_type_index: i,
603 active_general_blocks: 0,
604 committed_allocations: CommittedAllocationStatistics {
605 num_allocations: 0,
606 total_size: 0,
607 },
608 },
609 )
610 .collect::<Vec<_>>();
611
612 Ok(Self {
613 memory_types,
614 device,
615 debug_settings: desc.debug_settings,
616 allocation_sizes: desc.allocation_sizes,
617 })
618 }
619
620 pub fn allocate(&mut self, desc: &AllocationCreateDesc<'_>) -> Result<Allocation> {
621 let size = desc.size;
622 let alignment = desc.alignment;
623
624 #[cfg(feature = "std")]
625 let backtrace = Arc::new(if self.debug_settings.store_stack_traces {
626 Backtrace::force_capture()
627 } else {
628 Backtrace::disabled()
629 });
630
631 if self.debug_settings.log_allocations {
632 debug!(
633 "Allocating `{}` of {} bytes with an alignment of {}.",
634 &desc.name, size, alignment
635 );
636 #[cfg(feature = "std")]
637 if self.debug_settings.log_stack_traces {
638 let backtrace = Backtrace::force_capture();
639 debug!("Allocation stack trace: {backtrace}");
640 }
641 }
642
643 if size == 0 || !alignment.is_power_of_two() {
644 return Err(AllocationError::InvalidAllocationCreateDesc);
645 }
646
647 let memory_type = self
649 .memory_types
650 .iter_mut()
651 .find(|memory_type| {
652 let is_location_compatible = desc.location == MemoryLocation::Unknown
653 || desc.location == memory_type.memory_location;
654
655 let is_category_compatible = memory_type.heap_category == HeapCategory::All
656 || memory_type.heap_category == desc.resource_category.into();
657
658 is_location_compatible && is_category_compatible
659 })
660 .ok_or(AllocationError::NoCompatibleMemoryTypeFound)?;
661
662 memory_type.allocate(
663 &self.device,
664 desc,
665 #[cfg(feature = "std")]
666 backtrace,
667 &self.allocation_sizes,
668 )
669 }
670
671 pub fn free(&mut self, allocation: Allocation) -> Result<()> {
672 if self.debug_settings.log_frees {
673 let name = allocation.name.as_deref().unwrap_or("<null>");
674 debug!("Freeing `{name}`.");
675 #[cfg(feature = "std")]
676 if self.debug_settings.log_stack_traces {
677 let backtrace = Backtrace::force_capture();
678 debug!("Free stack trace: {backtrace}");
679 }
680 }
681
682 if allocation.is_null() {
683 return Ok(());
684 }
685
686 self.memory_types[allocation.memory_type_index].free(allocation)?;
687
688 Ok(())
689 }
690
691 pub fn rename_allocation(&mut self, allocation: &mut Allocation, name: &str) -> Result<()> {
692 allocation.name = Some(name.into());
693
694 if allocation.is_null() {
695 return Ok(());
696 }
697
698 let mem_type = &mut self.memory_types[allocation.memory_type_index];
699 let mem_block = mem_type.memory_blocks[allocation.memory_block_index]
700 .as_mut()
701 .ok_or_else(|| AllocationError::Internal("Memory block must be Some.".into()))?;
702
703 mem_block
704 .sub_allocator
705 .rename_allocation(allocation.chunk_id, name)?;
706
707 Ok(())
708 }
709
710 pub fn report_memory_leaks(&self, log_level: Level) {
711 for (mem_type_i, mem_type) in self.memory_types.iter().enumerate() {
712 for (block_i, mem_block) in mem_type.memory_blocks.iter().enumerate() {
713 if let Some(mem_block) = mem_block {
714 mem_block
715 .sub_allocator
716 .report_memory_leaks(log_level, mem_type_i, block_i);
717 }
718 }
719 }
720 }
721
722 fn d3d12_resource_desc_1(desc: &D3D12_RESOURCE_DESC) -> D3D12_RESOURCE_DESC1 {
723 D3D12_RESOURCE_DESC1 {
724 Dimension: desc.Dimension,
725 Alignment: desc.Alignment,
726 Width: desc.Width,
727 Height: desc.Height,
728 DepthOrArraySize: desc.DepthOrArraySize,
729 MipLevels: desc.MipLevels,
730 Format: desc.Format,
731 SampleDesc: desc.SampleDesc,
732 Layout: desc.Layout,
733 Flags: desc.Flags,
734 SamplerFeedbackMipRegion: D3D12_MIP_REGION::default(),
736 }
737 }
738
739 fn resource_allocation_info(
740 device: &ID3D12DeviceVersion,
741 desc: &ResourceCreateDesc<'_>,
742 ) -> D3D12_RESOURCE_ALLOCATION_INFO {
743 match device {
744 ID3D12DeviceVersion::Device(device) => unsafe {
745 device.GetResourceAllocationInfo(0, &[*desc.resource_desc])
746 },
747 ID3D12DeviceVersion::Device10(device) => unsafe {
748 device.GetResourceAllocationInfo(0, &[*desc.resource_desc])
749 },
750 ID3D12DeviceVersion::Device12(device) => unsafe {
751 let resource_desc1 = Self::d3d12_resource_desc_1(desc.resource_desc);
752
753 let resource_descs = &[resource_desc1];
754
755 let num_castable_formats = desc.castable_formats.len() as u32;
757 let num_castable_formats_array = &[num_castable_formats];
758
759 let castable_formats_array = &[desc.castable_formats.as_ptr()];
760
761 let (num_castable_formats_opt, castable_formats_opt) = if num_castable_formats > 0 {
762 (
763 Some(num_castable_formats_array.as_ptr()),
764 Some(castable_formats_array.as_ptr()),
765 )
766 } else {
767 (None, None)
768 };
769
770 device.GetResourceAllocationInfo3(
771 0,
772 resource_descs.len() as u32,
773 resource_descs.as_ptr(),
774 num_castable_formats_opt,
775 castable_formats_opt,
776 None,
777 )
778 },
779 }
780 }
781
782 pub fn create_resource(&mut self, desc: &ResourceCreateDesc<'_>) -> Result<Resource> {
785 match desc.resource_type {
786 ResourceType::Committed {
787 heap_properties,
788 heap_flags,
789 } => {
790 let mut result: Option<ID3D12Resource> = None;
791
792 let clear_value: Option<*const D3D12_CLEAR_VALUE> =
793 desc.clear_value.map(|v| -> *const _ { v });
794
795 if let Err(e) = unsafe {
796 match (&self.device, desc.initial_state_or_layout) {
797 (_, ResourceStateOrBarrierLayout::ResourceState(_))
798 if !desc.castable_formats.is_empty() =>
799 {
800 return Err(AllocationError::CastableFormatsRequiresEnhancedBarriers)
801 }
802 (
803 ID3D12DeviceVersion::Device12(device),
804 ResourceStateOrBarrierLayout::BarrierLayout(initial_layout),
805 ) => {
806 let resource_desc1 = Self::d3d12_resource_desc_1(desc.resource_desc);
807 device.CreateCommittedResource3(
808 *heap_properties,
809 *heap_flags,
810 &resource_desc1,
811 initial_layout,
812 clear_value,
813 None, Some(desc.castable_formats),
815 &mut result,
816 )
817 }
818 (_, ResourceStateOrBarrierLayout::BarrierLayout(_))
819 if !desc.castable_formats.is_empty() =>
820 {
821 return Err(AllocationError::CastableFormatsRequiresAtLeastDevice12)
822 }
823 (
824 ID3D12DeviceVersion::Device10(device),
825 ResourceStateOrBarrierLayout::BarrierLayout(initial_layout),
826 ) => {
827 let resource_desc1 = Self::d3d12_resource_desc_1(desc.resource_desc);
828
829 device.CreateCommittedResource3(
830 *heap_properties,
831 *heap_flags,
832 &resource_desc1,
833 initial_layout,
834 clear_value,
835 None, None,
837 &mut result,
838 )
839 }
840 (_, ResourceStateOrBarrierLayout::BarrierLayout(_)) => {
841 return Err(AllocationError::BarrierLayoutNeedsDevice10)
842 }
843 (device, ResourceStateOrBarrierLayout::ResourceState(initial_state)) => {
844 device.CreateCommittedResource(
845 *heap_properties,
846 *heap_flags,
847 desc.resource_desc,
848 initial_state,
849 clear_value,
850 &mut result,
851 )
852 }
853 }
854 } {
855 if e.code() == DXGI_ERROR_DEVICE_REMOVED {
856 return Err(AllocationError::Internal(format!(
857 "ID3D12Device::CreateCommittedResource DEVICE_REMOVED: {:?}",
858 unsafe { self.device.GetDeviceRemovedReason() }
859 )));
860 }
861 return Err(AllocationError::Internal(format!(
862 "ID3D12Device::CreateCommittedResource failed: {e}"
863 )));
864 }
865
866 let resource = result.expect("Allocation succeeded but no resource was returned?");
867
868 let allocation_info = Self::resource_allocation_info(&self.device, desc);
869
870 let memory_type = self
871 .memory_types
872 .iter_mut()
873 .find(|memory_type| {
874 let is_location_compatible = desc.memory_location
875 == MemoryLocation::Unknown
876 || desc.memory_location == memory_type.memory_location;
877
878 let is_category_compatible = memory_type.heap_category == HeapCategory::All
879 || memory_type.heap_category == desc.resource_category.into();
880
881 is_location_compatible && is_category_compatible
882 })
883 .ok_or(AllocationError::NoCompatibleMemoryTypeFound)?;
884
885 memory_type.committed_allocations.num_allocations += 1;
886 memory_type.committed_allocations.total_size += allocation_info.SizeInBytes;
887
888 Ok(Resource {
889 name: desc.name.into(),
890 allocation: None,
891 resource: Some(resource),
892 size: allocation_info.SizeInBytes,
893 memory_location: desc.memory_location,
894 memory_type_index: Some(memory_type.memory_type_index),
895 })
896 }
897 ResourceType::Placed => {
898 let allocation_desc = {
899 let allocation_info = Self::resource_allocation_info(&self.device, desc);
900
901 AllocationCreateDesc {
902 name: desc.name,
903 location: desc.memory_location,
904 size: allocation_info.SizeInBytes,
905 alignment: allocation_info.Alignment,
906 resource_category: desc.resource_category,
907 }
908 };
909
910 let allocation = self.allocate(&allocation_desc)?;
911
912 let mut result: Option<ID3D12Resource> = None;
913 if let Err(e) = unsafe {
914 match (&self.device, desc.initial_state_or_layout) {
915 (_, ResourceStateOrBarrierLayout::ResourceState(_))
916 if !desc.castable_formats.is_empty() =>
917 {
918 return Err(AllocationError::CastableFormatsRequiresEnhancedBarriers)
919 }
920 (
921 ID3D12DeviceVersion::Device12(device),
922 ResourceStateOrBarrierLayout::BarrierLayout(initial_layout),
923 ) => {
924 let resource_desc1 = Self::d3d12_resource_desc_1(desc.resource_desc);
925 device.CreatePlacedResource2(
926 allocation.heap(),
927 allocation.offset(),
928 &resource_desc1,
929 initial_layout,
930 None,
931 Some(desc.castable_formats),
932 &mut result,
933 )
934 }
935 (_, ResourceStateOrBarrierLayout::BarrierLayout(_))
936 if !desc.castable_formats.is_empty() =>
937 {
938 return Err(AllocationError::CastableFormatsRequiresAtLeastDevice12)
939 }
940 (
941 ID3D12DeviceVersion::Device10(device),
942 ResourceStateOrBarrierLayout::BarrierLayout(initial_layout),
943 ) => {
944 let resource_desc1 = Self::d3d12_resource_desc_1(desc.resource_desc);
945 device.CreatePlacedResource2(
946 allocation.heap(),
947 allocation.offset(),
948 &resource_desc1,
949 initial_layout,
950 None,
951 None,
952 &mut result,
953 )
954 }
955 (_, ResourceStateOrBarrierLayout::BarrierLayout(_)) => {
956 return Err(AllocationError::BarrierLayoutNeedsDevice10)
957 }
958 (device, ResourceStateOrBarrierLayout::ResourceState(initial_state)) => {
959 device.CreatePlacedResource(
960 allocation.heap(),
961 allocation.offset(),
962 desc.resource_desc,
963 initial_state,
964 None,
965 &mut result,
966 )
967 }
968 }
969 } {
970 if e.code() == DXGI_ERROR_DEVICE_REMOVED {
971 return Err(AllocationError::Internal(format!(
972 "ID3D12Device::CreatePlacedResource DEVICE_REMOVED: {:?}",
973 unsafe { self.device.GetDeviceRemovedReason() }
974 )));
975 }
976 return Err(AllocationError::Internal(format!(
977 "ID3D12Device::CreatePlacedResource failed: {e}"
978 )));
979 }
980
981 let resource = result.expect("Allocation succeeded but no resource was returned?");
982 let size = allocation.size();
983 Ok(Resource {
984 name: desc.name.into(),
985 allocation: Some(allocation),
986 resource: Some(resource),
987 size,
988 memory_location: desc.memory_location,
989 memory_type_index: None,
990 })
991 }
992 }
993 }
994
995 pub fn free_resource(&mut self, mut resource: Resource) -> Result<()> {
997 let _ = resource
1000 .resource
1001 .take()
1002 .expect("Resource was already freed.");
1003
1004 if let Some(allocation) = resource.allocation.take() {
1005 self.free(allocation)
1006 } else {
1007 if let Some(memory_type_index) = resource.memory_type_index {
1010 let memory_type = &mut self.memory_types[memory_type_index];
1011
1012 memory_type.committed_allocations.num_allocations -= 1;
1013 memory_type.committed_allocations.total_size -= resource.size;
1014 }
1015 Ok(())
1016 }
1017 }
1018
1019 pub fn generate_report(&self) -> AllocatorReport {
1020 let mut allocations = vec![];
1021 let mut blocks = vec![];
1022 let mut total_capacity_bytes = 0;
1023
1024 for memory_type in &self.memory_types {
1025 for block in memory_type.memory_blocks.iter().flatten() {
1026 total_capacity_bytes += block.size;
1027 let first_allocation = allocations.len();
1028 allocations.extend(block.sub_allocator.report_allocations());
1029 blocks.push(MemoryBlockReport {
1030 size: block.size,
1031 allocations: first_allocation..allocations.len(),
1032 });
1033 }
1034 }
1035
1036 let total_allocated_bytes = allocations.iter().map(|report| report.size).sum();
1037
1038 AllocatorReport {
1039 allocations,
1040 blocks,
1041 total_allocated_bytes,
1042 total_capacity_bytes,
1043 }
1044 }
1045
1046 pub fn capacity(&self) -> u64 {
1048 let mut total_capacity_bytes = 0;
1049
1050 for memory_type in &self.memory_types {
1051 for block in memory_type.memory_blocks.iter().flatten() {
1052 total_capacity_bytes += block.size;
1053 }
1054 }
1055
1056 total_capacity_bytes
1057 }
1058}
1059
1060impl fmt::Debug for Allocator {
1061 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1062 self.generate_report().fmt(f)
1063 }
1064}
1065
1066impl Drop for Allocator {
1067 fn drop(&mut self) {
1068 if self.debug_settings.log_leaks_on_shutdown {
1069 self.report_memory_leaks(Level::Warn);
1070 }
1071
1072 for mem_type in self.memory_types.iter_mut() {
1076 mem_type.memory_blocks.clear();
1077 }
1078 }
1079}