Skip to main content

wgpu_hal/vulkan/
memory.rs

1//! Device memory sub-allocation.
2//!
3//! gpu-allocator frees a block only after every allocation in it is freed,
4//! so short-lived resources get their own pool.
5
6use alloc::vec::Vec;
7use core::ops::Deref;
8
9use ash::vk;
10use wgpu_sync::Mutex;
11
12#[derive(Debug, Clone, Copy)]
13pub(super) enum MemoryPool {
14    General,
15    Transient,
16}
17
18impl MemoryPool {
19    pub(super) fn from_memory_flags(flags: crate::MemoryFlags) -> Self {
20        if flags.contains(crate::MemoryFlags::TRANSIENT) {
21            Self::Transient
22        } else {
23            Self::General
24        }
25    }
26}
27
28/// A sub-allocation that records which pool to free it to.
29#[derive(Debug)]
30pub struct MemoryAllocation {
31    pool: MemoryPool,
32    inner: gpu_allocator::vulkan::Allocation,
33}
34
35impl Deref for MemoryAllocation {
36    type Target = gpu_allocator::vulkan::Allocation;
37
38    fn deref(&self) -> &Self::Target {
39        &self.inner
40    }
41}
42
43pub(super) struct MemoryAllocators {
44    general: Mutex<gpu_allocator::vulkan::Allocator>,
45    transient: Mutex<gpu_allocator::vulkan::Allocator>,
46}
47
48impl MemoryAllocators {
49    pub(super) fn new(
50        instance: ash::Instance,
51        device: ash::Device,
52        physical_device: vk::PhysicalDevice,
53        buffer_device_address: bool,
54        allocation_sizes: crate::AllocationSizes,
55    ) -> Result<Self, gpu_allocator::AllocationError> {
56        let mut desc = gpu_allocator::vulkan::AllocatorCreateDesc {
57            instance,
58            device,
59            physical_device,
60            debug_settings: Default::default(),
61            buffer_device_address,
62            allocation_sizes: allocation_sizes.into(),
63        };
64        let general = gpu_allocator::vulkan::Allocator::new(&desc)?;
65        desc.allocation_sizes = allocation_sizes.transient().into();
66        let transient = gpu_allocator::vulkan::Allocator::new(&desc)?;
67        Ok(Self {
68            general: Mutex::new(general),
69            transient: Mutex::new(transient),
70        })
71    }
72
73    fn allocator(&self, pool: MemoryPool) -> &Mutex<gpu_allocator::vulkan::Allocator> {
74        match pool {
75            MemoryPool::General => &self.general,
76            MemoryPool::Transient => &self.transient,
77        }
78    }
79
80    pub(super) fn allocate(
81        &self,
82        pool: MemoryPool,
83        desc: &gpu_allocator::vulkan::AllocationCreateDesc<'_>,
84    ) -> Result<MemoryAllocation, gpu_allocator::AllocationError> {
85        let inner = self.allocator(pool).lock().allocate(desc)?;
86        Ok(MemoryAllocation { pool, inner })
87    }
88
89    pub(super) fn free(
90        &self,
91        allocation: MemoryAllocation,
92    ) -> Result<(), gpu_allocator::AllocationError> {
93        self.allocator(allocation.pool)
94            .lock()
95            .free(allocation.inner)
96    }
97
98    pub(super) fn generate_report(&self) -> wgt::AllocatorReport {
99        let mut allocations = Vec::new();
100        let mut blocks = Vec::new();
101        let mut total_allocated_bytes = 0;
102        let mut total_reserved_bytes = 0;
103
104        for allocator in [&self.general, &self.transient] {
105            let report = allocator.lock().generate_report();
106
107            // Each block's range indexes into its own report's allocation list.
108            let allocation_base = allocations.len();
109            allocations.extend(
110                report
111                    .allocations
112                    .into_iter()
113                    .map(|alloc| wgt::AllocationReport {
114                        name: alloc.name,
115                        offset: alloc.offset,
116                        size: alloc.size,
117                    }),
118            );
119            blocks.extend(
120                report
121                    .blocks
122                    .into_iter()
123                    .map(|block| wgt::MemoryBlockReport {
124                        size: block.size,
125                        allocations: (block.allocations.start + allocation_base)
126                            ..(block.allocations.end + allocation_base),
127                    }),
128            );
129            total_allocated_bytes += report.total_allocated_bytes;
130            total_reserved_bytes += report.total_capacity_bytes;
131        }
132
133        wgt::AllocatorReport {
134            allocations,
135            blocks,
136            total_allocated_bytes,
137            total_reserved_bytes,
138        }
139    }
140}