Skip to main content

alpha2_native_gpu/
memory_budget.rs

1//! Device memory budget the platform reports for the renderer's adapter.
2//!
3//! Sources: DXGI `QueryVideoMemoryInfo`, Vulkan `VK_EXT_memory_budget` device-local heaps, and
4//! Metal `recommendedMaxWorkingSetSize`. Other backends, and Vulkan without the extension, have
5//! none.
6
7/// The device memory budget of `adapter` in bytes, or `None` where the API reports none.
8#[must_use]
9pub fn adapter_memory_budget(adapter: &wgpu::Adapter) -> Option<u64> {
10    platform_budget(adapter).filter(|bytes| *bytes > 0)
11}
12
13/// Sum of the device-local heap budgets, when the device has `VK_EXT_memory_budget`.
14#[cfg(target_os = "linux")]
15fn platform_budget(adapter: &wgpu::Adapter) -> Option<u64> {
16    use ash::vk;
17
18    // SAFETY: the guard only borrows the live adapter for these queries.
19    let hal = unsafe { adapter.as_hal::<wgpu_hal::api::Vulkan>() }?;
20    let physical = hal.raw_physical_device();
21    let instance = hal.shared_instance().raw_instance();
22    // SAFETY: `physical` is a physical device of `instance`, which outlives the call.
23    let extensions = unsafe { instance.enumerate_device_extension_properties(physical) }.ok()?;
24    let has_budget = extensions
25        .iter()
26        .any(|extension| extension.extension_name_as_c_str() == Ok(ash::ext::memory_budget::NAME));
27    if !has_budget {
28        return None;
29    }
30    let mut budget = vk::PhysicalDeviceMemoryBudgetPropertiesEXT::default();
31    let mut properties = vk::PhysicalDeviceMemoryProperties2::default().push_next(&mut budget);
32    // SAFETY: the device has the extension, so the chained structure is valid for the call, and
33    // both structures outlive it. wgpu's instance is Vulkan 1.1+, which has the entry point.
34    unsafe { instance.get_physical_device_memory_properties2(physical, &mut properties) };
35    let heaps = properties.memory_properties;
36    let count = usize::try_from(heaps.memory_heap_count).ok()?;
37    let total = heaps.memory_heaps[..count.min(heaps.memory_heaps.len())]
38        .iter()
39        .zip(budget.heap_budget)
40        .filter(|(heap, _)| heap.flags.contains(vk::MemoryHeapFlags::DEVICE_LOCAL))
41        .map(|(_, bytes)| bytes)
42        .fold(0_u64, u64::saturating_add);
43    Some(total)
44}
45
46/// The budget of the adapter's local memory segment group.
47#[cfg(target_os = "windows")]
48fn platform_budget(adapter: &wgpu::Adapter) -> Option<u64> {
49    // SAFETY: the guard only borrows the live adapter for the query.
50    let hal = unsafe { adapter.as_hal::<wgpu_hal::api::Dx12>() }?;
51    let info = hal
52        .raw_adapter()
53        .query_video_memory_info(windows::Win32::Graphics::Dxgi::DXGI_MEMORY_SEGMENT_GROUP_LOCAL)
54        .ok()?;
55    Some(info.Budget)
56}
57
58#[cfg(target_os = "macos")]
59fn platform_budget(adapter: &wgpu::Adapter) -> Option<u64> {
60    use objc2_metal::MTLDevice;
61
62    // SAFETY: the guard only borrows the live adapter for the query.
63    let hal = unsafe { adapter.as_hal::<wgpu_hal::api::Metal>() }?;
64    Some(hal.raw_device().recommendedMaxWorkingSetSize())
65}
66
67#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
68fn platform_budget(_: &wgpu::Adapter) -> Option<u64> {
69    None
70}