Skip to content

Commit 02ec94b

Browse files
committed
[vk] add OOM checks before creating buffers, textures, query sets and acceleration structures
1 parent 558793a commit 02ec94b

File tree

1 file changed

+91
-1
lines changed

1 file changed

+91
-1
lines changed

wgpu-hal/src/vulkan/device.rs

+91-1
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,84 @@ impl super::Device {
10211021
pub fn shared_instance(&self) -> &super::InstanceShared {
10221022
&self.shared.instance
10231023
}
1024+
1025+
fn check_for_oom(&self, needs_host_access: bool, size: u64) -> Result<(), crate::DeviceError> {
1026+
if self
1027+
.shared
1028+
.enabled_extensions
1029+
.contains(&ext::memory_budget::NAME)
1030+
{
1031+
let get_physical_device_properties = self
1032+
.shared
1033+
.instance
1034+
.get_physical_device_properties
1035+
.as_ref()
1036+
.unwrap();
1037+
1038+
let mut memory_budget_properties =
1039+
vk::PhysicalDeviceMemoryBudgetPropertiesEXT::default();
1040+
1041+
let mut memory_properties = vk::PhysicalDeviceMemoryProperties2::default()
1042+
.push_next(&mut memory_budget_properties);
1043+
1044+
unsafe {
1045+
get_physical_device_properties.get_physical_device_memory_properties2(
1046+
self.shared.physical_device,
1047+
&mut memory_properties,
1048+
);
1049+
}
1050+
1051+
let mut host_visible_heaps = [false; vk::MAX_MEMORY_HEAPS];
1052+
let mut device_local_heaps = [false; vk::MAX_MEMORY_HEAPS];
1053+
1054+
let memory_properties = memory_properties.memory_properties;
1055+
1056+
for i in 0..memory_properties.memory_type_count {
1057+
let memory_type = memory_properties.memory_types[i as usize];
1058+
let flags = memory_type.property_flags;
1059+
1060+
if flags.intersects(
1061+
vk::MemoryPropertyFlags::LAZILY_ALLOCATED | vk::MemoryPropertyFlags::PROTECTED,
1062+
) {
1063+
continue; // not used by gpu-alloc
1064+
}
1065+
1066+
if flags.contains(vk::MemoryPropertyFlags::HOST_VISIBLE) {
1067+
host_visible_heaps[memory_type.heap_index as usize] = true;
1068+
}
1069+
1070+
if flags.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL) {
1071+
device_local_heaps[memory_type.heap_index as usize] = true;
1072+
}
1073+
}
1074+
1075+
let heaps = if needs_host_access {
1076+
host_visible_heaps
1077+
} else {
1078+
device_local_heaps
1079+
};
1080+
1081+
// NOTE: We might end up checking multiple heaps since gpu-alloc doesn't have a way
1082+
// for us to query the heap the resource will end up on. But this is unlikely,
1083+
// there is usually only one heap on integrated GPUs and two on dedicated GPUs.
1084+
1085+
for (i, check) in heaps.iter().enumerate() {
1086+
if !check {
1087+
continue;
1088+
}
1089+
1090+
let heap_usage = memory_budget_properties.heap_usage[i];
1091+
let heap_budget = memory_budget_properties.heap_budget[i];
1092+
1093+
// Make sure we don't exceed 90% of the budget
1094+
if heap_usage + size >= heap_budget / 100 * 90 {
1095+
return Err(crate::DeviceError::OutOfMemory);
1096+
}
1097+
}
1098+
}
1099+
1100+
Ok(())
1101+
}
10241102
}
10251103

10261104
impl crate::Device for super::Device {
@@ -1066,6 +1144,10 @@ impl crate::Device for super::Device {
10661144
desc.memory_flags.contains(crate::MemoryFlags::TRANSIENT),
10671145
);
10681146

1147+
let needs_host_access = alloc_usage.contains(gpu_alloc::UsageFlags::HOST_ACCESS);
1148+
1149+
self.check_for_oom(needs_host_access, req.size)?;
1150+
10691151
let alignment_mask = req.alignment - 1;
10701152

10711153
let block = unsafe {
@@ -1176,6 +1258,8 @@ impl crate::Device for super::Device {
11761258
) -> Result<super::Texture, crate::DeviceError> {
11771259
let image = self.create_image_without_memory(desc, None)?;
11781260

1261+
self.check_for_oom(false, image.requirements.size)?;
1262+
11791263
let block = unsafe {
11801264
self.mem_allocator.lock().alloc(
11811265
&*self.shared,
@@ -2435,6 +2519,10 @@ impl crate::Device for super::Device {
24352519
&self,
24362520
desc: &wgt::QuerySetDescriptor<crate::Label>,
24372521
) -> Result<super::QuerySet, crate::DeviceError> {
2522+
// Assume each query is 256 bytes.
2523+
// On an AMD W6800 with driver version 32.0.12030.9, occlusion queries are 256.
2524+
self.check_for_oom(true, desc.count as u64 * 256)?;
2525+
24382526
let (vk_type, pipeline_statistics) = match desc.ty {
24392527
wgt::QueryType::Occlusion => (
24402528
vk::QueryType::OCCLUSION,
@@ -2764,6 +2852,8 @@ impl crate::Device for super::Device {
27642852
.map_err(super::map_host_device_oom_and_ioca_err)?;
27652853
let req = self.shared.raw.get_buffer_memory_requirements(raw_buffer);
27662854

2855+
self.check_for_oom(false, req.size)?;
2856+
27672857
let block = self.mem_allocator.lock().alloc(
27682858
&*self.shared,
27692859
gpu_alloc::Request {
@@ -2808,7 +2898,7 @@ impl crate::Device for super::Device {
28082898
.shared
28092899
.raw
28102900
.create_query_pool(&vk_info, None)
2811-
.map_err(super::map_host_oom_and_ioca_err)?;
2901+
.map_err(super::map_host_device_oom_err)?;
28122902
Some(raw)
28132903
} else {
28142904
None

0 commit comments

Comments
 (0)