From 3532f16227900b913c91a69c767587d85a5a43b5 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 11:07:31 -0500 Subject: [PATCH 01/16] Added from_raw_managed, hopefully made stuff work --- wgpu-hal/src/vulkan/device.rs | 43 +++++++++++++----------- wgpu-hal/src/vulkan/mod.rs | 63 +++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 77b9ebe9244..5840748e621 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -808,17 +808,6 @@ impl super::Device { }) } - /// # Safety - /// - /// - `vk_buffer`'s memory must be managed by the caller - /// - Externally imported buffers can't be mapped by `wgpu` - pub unsafe fn buffer_from_raw(vk_buffer: vk::Buffer) -> super::Buffer { - super::Buffer { - raw: vk_buffer, - block: None, - } - } - fn create_shader_module_impl( &self, spv: &[u32], @@ -1153,7 +1142,7 @@ impl crate::Device for super::Device { Ok(super::Buffer { raw, - block: Some(Mutex::new(block)), + block: Some(Mutex::new(super::BufferMemoryBacking::Managed(block))), }) } unsafe fn destroy_buffer(&self, buffer: super::Buffer) { @@ -1161,7 +1150,14 @@ impl crate::Device for super::Device { if let Some(block) = buffer.block { let block = block.into_inner(); self.counters.buffer_memory.sub(block.size() as isize); - unsafe { self.mem_allocator.lock().dealloc(&*self.shared, block) }; + match block { + super::BufferMemoryBacking::Managed(block) => unsafe { + self.mem_allocator.lock().dealloc(&*self.shared, block) + }, + super::BufferMemoryBacking::VulkanMemory { memory, .. } => unsafe { + self.shared.raw.free_memory(memory, None); + }, + } } self.counters.buffers.sub(1); @@ -1179,18 +1175,27 @@ impl crate::Device for super::Device { if let Some(ref block) = buffer.block { let size = range.end - range.start; let mut block = block.lock(); - let ptr = unsafe { block.map(&*self.shared, range.start, size as usize)? }; - let is_coherent = block - .props() - .contains(gpu_alloc::MemoryPropertyFlags::HOST_COHERENT); - Ok(crate::BufferMapping { ptr, is_coherent }) + if let super::BufferMemoryBacking::Managed(block) = &mut *block { + let ptr = unsafe { block.map(&*self.shared, range.start, size as usize)? }; + let is_coherent = block + .props() + .contains(gpu_alloc::MemoryPropertyFlags::HOST_COHERENT); + Ok(crate::BufferMapping { ptr, is_coherent }) + } else { + crate::hal_usage_error("tried to map externally created buffer") + } } else { crate::hal_usage_error("tried to map external buffer") } } unsafe fn unmap_buffer(&self, buffer: &super::Buffer) { if let Some(ref block) = buffer.block { - unsafe { block.lock().unmap(&*self.shared) }; + match &mut *block.lock() { + super::BufferMemoryBacking::Managed(block) => unsafe { block.unmap(&*self.shared) }, + super::BufferMemoryBacking::VulkanMemory { .. } => { + crate::hal_usage_error("tried to unmap externally created buffer") + } + }; } else { crate::hal_usage_error("tried to unmap external buffer") } diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 3d4e5cacee4..7a336a55083 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -776,11 +776,70 @@ impl Drop for Queue { unsafe { self.relay_semaphores.lock().destroy(&self.device.raw) }; } } - +#[derive(Debug)] +enum BufferMemoryBacking { + Managed(gpu_alloc::MemoryBlock), + VulkanMemory { + memory: vk::DeviceMemory, + offset: u64, + size: u64, + }, +} +impl BufferMemoryBacking { + fn memory(&self) -> &vk::DeviceMemory { + match self { + Self::Managed(m) => m.memory(), + Self::VulkanMemory { memory, .. } => memory, + } + } + fn offset(&self) -> u64 { + match self { + Self::Managed(m) => m.offset(), + Self::VulkanMemory { offset, .. } => *offset, + } + } + fn size(&self) -> u64 { + match self { + Self::Managed(m) => m.size(), + Self::VulkanMemory { size, .. } => *size, + } + } +} #[derive(Debug)] pub struct Buffer { raw: vk::Buffer, - block: Option>>, + block: Option>, +} +impl Buffer { + /// # Safety + /// + /// - `vk_buffer`'s memory must be managed by the caller + /// - Externally imported buffers can't be mapped by `wgpu` + pub unsafe fn from_raw(vk_buffer: vk::Buffer) -> Self { + Self { + raw: vk_buffer, + block: None, + } + } + /// # Safety + /// - `memory` must not be used further by the caller + /// - Externally imported buffers can't be mapped by `wgpu` + /// - `offset` and `size` must be valid with the allocation of `memory` + pub unsafe fn from_raw_managed( + vk_buffer: vk::Buffer, + memory: vk::DeviceMemory, + offset: u64, + size: u64, + ) -> Self { + Self { + raw: vk_buffer, + block: Some(Mutex::new(BufferMemoryBacking::VulkanMemory { + memory, + offset, + size, + })), + } + } } impl crate::DynBuffer for Buffer {} From fcfd6ce5706466a2855b50ea4324b5cb44d0bd1e Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 11:11:42 -0500 Subject: [PATCH 02/16] Added reporting of VK_KHR_external_memory_fd --- wgpu-hal/src/vulkan/adapter.rs | 4 ++++ wgpu-types/src/features.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index acd32ff6e7e..b1f68a7cee6 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -859,6 +859,10 @@ impl PhysicalDeviceFeatures { F::VULKAN_EXTERNAL_MEMORY_WIN32, caps.supports_extension(khr::external_memory_win32::NAME), ); + features.set( + F::VULKAN_EXTERNAL_MEMORY_FD, + caps.supports_extension(khr::external_memory_fd::NAME), + ); features.set( F::EXPERIMENTAL_MESH_SHADER, caps.supports_extension(ext::mesh_shader::NAME), diff --git a/wgpu-types/src/features.rs b/wgpu-types/src/features.rs index b614b8cb652..a6f24c36c61 100644 --- a/wgpu-types/src/features.rs +++ b/wgpu-types/src/features.rs @@ -1244,6 +1244,16 @@ bitflags_array! { /// /// [BlasTriangleGeometrySizeDescriptor::vertex_format]: super::BlasTriangleGeometrySizeDescriptor const EXTENDED_ACCELERATION_STRUCTURE_VERTEX_FORMATS = 1 << 51; + + /// Allows using the [VK_KHR_external_memory_fd] Vulkan extension. + /// + /// Supported platforms: + /// - Vulkan (with [VK_KHR_external_memory_fd]) + /// + /// This is a native only feature. + /// + /// [VK_KHR_external_memory_fd]: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_external_memory_fd.html + const VULKAN_EXTERNAL_MEMORY_FD = 1 << 45; } /// Features that are not guaranteed to be supported. From cbb0adb31e1554cb447f4c2136614c19f1eb5e11 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 16:08:41 -0500 Subject: [PATCH 03/16] Initial external memory fd implementation --- .../standalone/custom_backend/src/custom.rs | 9 + tests/tests/wgpu-gpu/external_memory.rs | 0 tests/tests/wgpu-gpu/main.rs | 1 + wgpu-core/src/device/global.rs | 56 +++++- wgpu-core/src/device/resource.rs | 186 ++++++++++++++++++ wgpu-core/src/resource.rs | 4 + wgpu-hal/src/dynamic/device.rs | 17 ++ wgpu-hal/src/gles/device.rs | 8 + wgpu-hal/src/lib.rs | 7 + wgpu-hal/src/noop/mod.rs | 8 + wgpu-hal/src/vulkan/adapter.rs | 14 +- wgpu-hal/src/vulkan/device.rs | 130 ++++++++++++ wgpu-hal/src/vulkan/instance.rs | 15 ++ wgpu-hal/src/vulkan/mod.rs | 1 + wgpu/src/api/device.rs | 26 +++ wgpu/src/backend/wgpu_core.rs | 27 +++ wgpu/src/dispatch.rs | 6 + 17 files changed, 509 insertions(+), 6 deletions(-) create mode 100644 tests/tests/wgpu-gpu/external_memory.rs diff --git a/examples/standalone/custom_backend/src/custom.rs b/examples/standalone/custom_backend/src/custom.rs index ec57413ece8..542d943b3f5 100644 --- a/examples/standalone/custom_backend/src/custom.rs +++ b/examples/standalone/custom_backend/src/custom.rs @@ -180,6 +180,15 @@ impl DeviceInterface for CustomDevice { unimplemented!() } + fn create_buffer_external_memory_fd( + &self, + _fd: i32, + _offset: u64, + _desc: &wgpu::BufferDescriptor<'_>, + ) -> wgpu::custom::DispatchBuffer { + unimplemented!() + } + fn create_texture(&self, _desc: &wgpu::TextureDescriptor<'_>) -> wgpu::custom::DispatchTexture { unimplemented!() } diff --git a/tests/tests/wgpu-gpu/external_memory.rs b/tests/tests/wgpu-gpu/external_memory.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/tests/wgpu-gpu/main.rs b/tests/tests/wgpu-gpu/main.rs index 8fd24c43435..9b02625d552 100644 --- a/tests/tests/wgpu-gpu/main.rs +++ b/tests/tests/wgpu-gpu/main.rs @@ -28,6 +28,7 @@ mod dispatch_workgroups_indirect; mod draw_indirect; mod dual_source_blending; mod encoder; +mod external_memory; mod external_texture; mod float32_filterable; mod image_atomics; diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 5e6e4fc463e..08d3d4f2944 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -142,6 +142,58 @@ impl Global { (id, Some(error)) } + pub fn device_create_buffer_external_memory_fd( + &self, + device_id: DeviceId, + fd: i32, + offset: u64, + desc: &resource::BufferDescriptor, + id_in: Option, + ) -> (id::BufferId, Option) { + profiling::scope!("Device::create_buffer_external_memory_fd"); + + let hub = &self.hub; + let fid = hub.buffers.prepare(id_in); + + let error = 'error: { + let device = self.hub.devices.get(device_id); + + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + let mut desc = desc.clone(); + let mapped_at_creation = core::mem::replace(&mut desc.mapped_at_creation, false); + if mapped_at_creation && !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) { + desc.usage |= wgt::BufferUsages::COPY_DST; + } + trace.add(trace::Action::CreateBuffer(fid.id(), desc)); + } + + let buffer = match device.create_buffer_external_memory_fd(fd, offset, desc) { + Ok(buffer) => buffer, + Err(e) => { + break 'error e; + } + }; + + let id = fid.assign(Fallible::Valid(buffer)); + + api_log!( + "Device::create_buffer_external_memory_fd({fd}, {offset}, {:?}{}) -> {id:?}", + desc.label.as_deref().unwrap_or(""), + if desc.mapped_at_creation { + ", mapped_at_creation" + } else { + "" + } + ); + + return (id, None); + }; + + let id = fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); + (id, Some(error)) + } + /// Assign `id_in` an error with the given `label`. /// /// Ensure that future attempts to use `id_in` as a buffer ID will propagate @@ -390,7 +442,7 @@ impl Global { desc: &resource::BufferDescriptor, id_in: Option, ) -> (id::BufferId, Option) { - profiling::scope!("Device::create_buffer"); + profiling::scope!("Device::create_buffer_from_hal"); let hub = &self.hub; let fid = hub.buffers.prepare(id_in); @@ -407,7 +459,7 @@ impl Global { let (buffer, err) = device.create_buffer_from_hal(Box::new(hal_buffer), desc); let id = fid.assign(buffer); - api_log!("Device::create_buffer -> {id:?}"); + api_log!("Device::create_buffer_from_hal -> {id:?}"); (id, err) } diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 53e9586da64..6550c6a6f9c 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -779,6 +779,192 @@ impl Device { Ok(buffer) } + pub(crate) fn create_buffer_external_memory_fd( + self: &Arc, + fd: i32, + offset: u64, + desc: &resource::BufferDescriptor, + ) -> Result, resource::CreateBufferError> { + self.check_is_valid()?; + + self.require_features(wgt::Features::VULKAN_EXTERNAL_MEMORY_FD) + .map_err(|_| resource::CreateBufferError::ExternalMemoryFeatureNotEnabled)?; + + if desc.size > self.limits.max_buffer_size { + return Err(resource::CreateBufferError::MaxBufferSize { + requested: desc.size, + maximum: self.limits.max_buffer_size, + }); + } + + if desc.usage.contains(wgt::BufferUsages::INDEX) + && desc.usage.contains( + wgt::BufferUsages::VERTEX + | wgt::BufferUsages::UNIFORM + | wgt::BufferUsages::INDIRECT + | wgt::BufferUsages::STORAGE, + ) + { + self.require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)?; + } + if desc.mapped_at_creation { + return Err(resource::CreateBufferError::ExternalMemoryMappedAtCreation); + } + + if desc.usage.is_empty() + || desc.usage.contains_unknown_bits() + || desc + .usage + .intersects(wgt::BufferUsages::MAP_READ | wgt::BufferUsages::MAP_WRITE) + { + return Err(resource::CreateBufferError::InvalidUsage(desc.usage)); + } + + if !self + .features + .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS) + { + use wgt::BufferUsages as Bu; + let write_mismatch = desc.usage.contains(Bu::MAP_WRITE) + && !(Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage); + let read_mismatch = desc.usage.contains(Bu::MAP_READ) + && !(Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage); + if write_mismatch || read_mismatch { + return Err(resource::CreateBufferError::UsageMismatch(desc.usage)); + } + } + + let mut usage = conv::map_buffer_usage(desc.usage); + + if desc.usage.contains(wgt::BufferUsages::INDIRECT) { + self.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?; + // We are going to be reading from it, internally; + // when validating the content of the buffer + usage |= wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE; + } + + if desc.usage.contains(wgt::BufferUsages::QUERY_RESOLVE) { + usage |= TIMESTAMP_NORMALIZATION_BUFFER_USES; + } + + if desc.mapped_at_creation { + if desc.size % wgt::COPY_BUFFER_ALIGNMENT != 0 { + return Err(resource::CreateBufferError::UnalignedSize); + } + if !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) { + // we are going to be copying into it, internally + usage |= wgt::BufferUses::COPY_DST; + } + } else { + // We are required to zero out (initialize) all memory. This is done + // on demand using clear_buffer which requires write transfer usage! + usage |= wgt::BufferUses::COPY_DST; + } + + let actual_size = if desc.size == 0 { + wgt::COPY_BUFFER_ALIGNMENT + } else if desc.usage.contains(wgt::BufferUsages::VERTEX) { + // Bumping the size by 1 so that we can bind an empty range at the + // end of the buffer. + desc.size + 1 + } else { + desc.size + }; + let clear_remainder = actual_size % wgt::COPY_BUFFER_ALIGNMENT; + let aligned_size = if clear_remainder != 0 { + actual_size + wgt::COPY_BUFFER_ALIGNMENT - clear_remainder + } else { + actual_size + }; + + let hal_desc = hal::BufferDescriptor { + label: desc.label.to_hal(self.instance_flags), + size: aligned_size, + usage, + memory_flags: hal::MemoryFlags::empty(), + }; + let buffer = unsafe { + self.raw() + .create_buffer_external_memory_fd(fd, offset, &hal_desc) + } + .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?; + + let timestamp_normalization_bind_group = Snatchable::new( + self.timestamp_normalizer + .get() + .unwrap() + .create_normalization_bind_group( + self, + &*buffer, + desc.label.as_deref(), + desc.size, + desc.usage, + )?, + ); + + let indirect_validation_bind_groups = + self.create_indirect_validation_bind_groups(buffer.as_ref(), desc.size, desc.usage)?; + + let buffer = Buffer { + raw: Snatchable::new(buffer), + device: self.clone(), + usage: desc.usage, + size: desc.size, + initialization_status: RwLock::new( + rank::BUFFER_INITIALIZATION_STATUS, + BufferInitTracker::new(aligned_size), + ), + map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle), + label: desc.label.to_string(), + tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()), + bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()), + timestamp_normalization_bind_group, + indirect_validation_bind_groups, + }; + + let buffer = Arc::new(buffer); + + let buffer_use = if !desc.mapped_at_creation { + wgt::BufferUses::empty() + } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) { + // buffer is mappable, so we are just doing that at start + let map_size = buffer.size; + let mapping = if map_size == 0 { + hal::BufferMapping { + ptr: core::ptr::NonNull::dangling(), + is_coherent: true, + } + } else { + let snatch_guard: SnatchGuard = self.snatchable_lock.read(); + map_buffer(&buffer, 0, map_size, HostMap::Write, &snatch_guard)? + }; + *buffer.map_state.lock() = resource::BufferMapState::Active { + mapping, + range: 0..map_size, + host: HostMap::Write, + }; + wgt::BufferUses::MAP_WRITE + } else { + let mut staging_buffer = + StagingBuffer::new(self, wgt::BufferSize::new(aligned_size).unwrap())?; + + // Zero initialize memory and then mark the buffer as initialized + // (it's guaranteed that this is the case by the time the buffer is usable) + staging_buffer.write_zeros(); + buffer.initialization_status.write().drain(0..aligned_size); + + *buffer.map_state.lock() = resource::BufferMapState::Init { staging_buffer }; + wgt::BufferUses::COPY_DST + }; + + self.trackers + .lock() + .buffers + .insert_single(&buffer, buffer_use); + + Ok(buffer) + } + pub(crate) fn create_texture_from_hal( self: &Arc, hal_texture: Box, diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 46be1c1e689..8ffc0a32a20 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -777,6 +777,10 @@ pub enum CreateBufferError { MissingDownlevelFlags(#[from] MissingDownlevelFlags), #[error("Failed to create bind group for indirect buffer validation: {0}")] IndirectValidationBindGroup(DeviceError), + #[error("Buffers created with `create_buffer_external_memory_fd` cannot be maped at creation")] + ExternalMemoryMappedAtCreation, + #[error("The VULKAN_EXTERNAL_MEMORY_FD feature must be enabled in order to create buffers from external memory file descriptors")] + ExternalMemoryFeatureNotEnabled, } crate::impl_resource_type!(Buffer); diff --git a/wgpu-hal/src/dynamic/device.rs b/wgpu-hal/src/dynamic/device.rs index fd7c10f254d..dcdf60bb7b0 100644 --- a/wgpu-hal/src/dynamic/device.rs +++ b/wgpu-hal/src/dynamic/device.rs @@ -22,6 +22,12 @@ pub trait DynDevice: DynResource { &self, desc: &BufferDescriptor, ) -> Result, DeviceError>; + unsafe fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &BufferDescriptor, + ) -> Result, DeviceError>; unsafe fn destroy_buffer(&self, buffer: Box); unsafe fn add_raw_buffer(&self, buffer: &dyn DynBuffer); @@ -182,6 +188,17 @@ impl DynDevice for D { ) -> Result, DeviceError> { unsafe { D::create_buffer(self, desc) }.map(|b| -> Box { Box::new(b) }) } + unsafe fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &BufferDescriptor, + ) -> Result, DeviceError> { + unsafe { + D::create_buffer_external_memory_fd(self, fd, offset, desc) + .map(|b| -> Box { Box::new(b) }) + } + } unsafe fn destroy_buffer(&self, buffer: Box) { unsafe { D::destroy_buffer(self, buffer.unbox()) }; diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index 347904c6ec7..28233634d57 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -640,6 +640,14 @@ impl crate::Device for super::Device { offset_of_current_mapping: Arc::new(MaybeMutex::new(0)), }) } + unsafe fn create_buffer_external_memory_fd( + &self, + _fd: i32, + _offset: u64, + _desc: &crate::BufferDescriptor, + ) -> Result<::Buffer, crate::DeviceError> { + unreachable!() + } unsafe fn destroy_buffer(&self, buffer: super::Buffer) { if let Some(raw) = buffer.raw { diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 4184c8c6f1b..90ccd1761ac 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -735,6 +735,13 @@ pub trait Device: WasmNotSendSync { desc: &BufferDescriptor, ) -> Result<::Buffer, DeviceError>; + unsafe fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &BufferDescriptor, + ) -> Result<::Buffer, DeviceError>; + /// Free `buffer` and any GPU resources it owns. /// /// Note that backends are allowed to allocate GPU memory for buffers from diff --git a/wgpu-hal/src/noop/mod.rs b/wgpu-hal/src/noop/mod.rs index d0192cb5168..8d4b51a5ee8 100644 --- a/wgpu-hal/src/noop/mod.rs +++ b/wgpu-hal/src/noop/mod.rs @@ -286,6 +286,14 @@ impl crate::Device for Context { unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult { Buffer::new(desc) } + unsafe fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &crate::BufferDescriptor, + ) -> Result<::Buffer, crate::DeviceError> { + unreachable!() + } unsafe fn destroy_buffer(&self, buffer: Buffer) {} unsafe fn add_raw_buffer(&self, _buffer: &Buffer) {} diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index b1f68a7cee6..352055d653e 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -537,11 +537,12 @@ impl PhysicalDeviceFeatures { /// [`DownlevelFlags`]: wgt::DownlevelFlags fn to_wgpu( &self, - instance: &ash::Instance, + instance_shared: &super::InstanceShared, phd: vk::PhysicalDevice, caps: &PhysicalDeviceProperties, ) -> (wgt::Features, wgt::DownlevelFlags) { use wgt::{DownlevelFlags as Df, Features as F}; + let instance = &instance_shared.raw; let mut features = F::empty() | F::SPIRV_SHADER_PASSTHROUGH | F::MAPPABLE_PRIMARY_BUFFERS @@ -857,11 +858,13 @@ impl PhysicalDeviceFeatures { features.set( F::VULKAN_EXTERNAL_MEMORY_WIN32, - caps.supports_extension(khr::external_memory_win32::NAME), + caps.supports_extension(khr::external_memory_win32::NAME) + && instance_shared.external_memory_capabilities.is_some(), ); features.set( F::VULKAN_EXTERNAL_MEMORY_FD, - caps.supports_extension(khr::external_memory_fd::NAME), + caps.supports_extension(khr::external_memory_fd::NAME) + && instance_shared.external_memory_capabilities.is_some(), ); features.set( F::EXPERIMENTAL_MESH_SHADER, @@ -1002,6 +1005,9 @@ impl PhysicalDeviceProperties { // we require that one already. extensions.push(khr::_16bit_storage::NAME); } + // These aren't strictly required except for some cases of external memory usage. + extensions.push(khr::get_memory_requirements2::NAME); + extensions.push(khr::dedicated_allocation::NAME); } if self.device_api_version < vk::API_VERSION_1_2 { @@ -1628,7 +1634,7 @@ impl super::Instance { backend: wgt::Backend::Vulkan, }; let (available_features, downlevel_flags) = - phd_features.to_wgpu(&self.shared.raw, phd, &phd_capabilities); + phd_features.to_wgpu(&self.shared, phd, &phd_capabilities); let mut workarounds = super::Workarounds::empty(); { // TODO: only enable for particular devices diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 5840748e621..7367956723a 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1145,6 +1145,15 @@ impl crate::Device for super::Device { block: Some(Mutex::new(super::BufferMemoryBacking::Managed(block))), }) } + unsafe fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &crate::BufferDescriptor, + ) -> Result<::Buffer, crate::DeviceError> { + unsafe { self.create_buffer_external_memory(fd as i64, offset, desc, false) } + } + unsafe fn destroy_buffer(&self, buffer: super::Buffer) { unsafe { self.shared.raw.destroy_buffer(buffer.raw, None) }; if let Some(block) = buffer.block { @@ -2999,6 +3008,127 @@ impl crate::Device for super::Device { Ok(()) } } +impl super::Device { + unsafe fn create_buffer_external_memory( + &self, + // larger of i32, isize + handle: i64, + offset: u64, + desc: &crate::BufferDescriptor, + is_win32: bool, + ) -> Result<<::A as crate::Api>::Buffer, crate::DeviceError> { + let buffer_usage = conv::map_buffer_usage(desc.usage); + + let mut external_properties = vk::ExternalBufferProperties::default(); + unsafe { + let caps = self + .shared_instance() + .external_memory_capabilities + .as_ref() + .unwrap(); + let info = vk::PhysicalDeviceExternalBufferInfoKHR::default() + .flags(vk::BufferCreateFlags::empty()); + (caps.fp().get_physical_device_external_buffer_properties_khr)( + self.raw_physical_device(), + &info, + &mut external_properties, + ); + } + if !external_properties + .external_memory_properties + .external_memory_features + .contains(vk::ExternalMemoryFeatureFlags::IMPORTABLE_KHR) + { + return Err(crate::DeviceError::Unexpected); + } + let needs_dedicated = external_properties + .external_memory_properties + .external_memory_features + .contains(vk::ExternalMemoryFeatureFlags::DEDICATED_ONLY_KHR); + + let vk_info = vk::BufferCreateInfo::default() + .size(desc.size) + .usage(buffer_usage) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + let raw = unsafe { + self.shared + .raw + .create_buffer(&vk_info, None) + .map_err(super::map_host_device_oom_and_ioca_err)? + }; + + let mut alloc_usage = if desc + .usage + .intersects(wgt::BufferUses::MAP_READ | wgt::BufferUses::MAP_WRITE) + { + let mut flags = gpu_alloc::UsageFlags::HOST_ACCESS; + //TODO: find a way to use `crate::MemoryFlags::PREFER_COHERENT` + flags.set( + gpu_alloc::UsageFlags::DOWNLOAD, + desc.usage.contains(wgt::BufferUses::MAP_READ), + ); + flags.set( + gpu_alloc::UsageFlags::UPLOAD, + desc.usage.contains(wgt::BufferUses::MAP_WRITE), + ); + flags + } else { + gpu_alloc::UsageFlags::FAST_DEVICE_ACCESS + }; + alloc_usage.set( + gpu_alloc::UsageFlags::TRANSIENT, + desc.memory_flags.contains(crate::MemoryFlags::TRANSIENT), + ); + + let mut dedicated_alloc_info = vk::MemoryDedicatedAllocateInfoKHR::default().buffer(raw); + let memory = if is_win32 { + let mut import_info = vk::ImportMemoryWin32HandleInfoKHR::default() + .handle(handle as isize) + .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32_KHR); + let mut allocate_info = vk::MemoryAllocateInfo::default().push_next(&mut import_info); + if needs_dedicated { + allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); + } + unsafe { self.raw_device().allocate_memory(&allocate_info, None) } + } else { + let mut import_info = vk::ImportMemoryFdInfoKHR::default() + .fd(handle as i32) + .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD_KHR); + let mut allocate_info = vk::MemoryAllocateInfo::default().push_next(&mut import_info); + if needs_dedicated { + allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); + } + unsafe { self.raw_device().allocate_memory(&allocate_info, None) } + } + .map_err(|_| crate::DeviceError::Unexpected) + .inspect_err(|_| unsafe { self.raw_device().destroy_buffer(raw, None) })?; + + unsafe { self.shared.raw.bind_buffer_memory(raw, memory, offset) } + .map_err(super::map_host_device_oom_and_ioca_err) + .inspect_err(|_| { + unsafe { self.shared.raw.destroy_buffer(raw, None) }; + })?; + + if let Some(label) = desc.label { + unsafe { self.shared.set_object_name(raw, label) }; + } + + self.counters.buffer_memory.add(desc.size as isize); + self.counters.buffers.add(1); + + Ok(super::Buffer { + raw, + block: Some(Mutex::new( + crate::vulkan::BufferMemoryBacking::VulkanMemory { + memory, + offset, + size: desc.size, + }, + )), + }) + } +} impl super::DeviceShared { pub(super) fn new_binary_semaphore(&self) -> Result { diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index c3ce109c042..1c53d6a0142 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -319,6 +319,9 @@ impl super::Instance { // so that we don't have to conditionally use the functions provided by the 1.1 instance extensions.push(khr::get_physical_device_properties2::NAME); + // This is required so we can check if a physical device might require dedicated memory for a given allocation. + extensions.push(khr::external_memory_capabilities::NAME); + // Only keep available extensions. extensions.retain(|&ext| { if instance_extensions @@ -398,6 +401,17 @@ impl super::Instance { None }; + let external_memory_capabilities = + if extensions.contains(&khr::external_memory_capabilities::NAME) { + log::debug!("Enabling external memory capabilities"); + Some(khr::external_memory_capabilities::Instance::new( + &entry, + &raw_instance, + )) + } else { + None + }; + let drop_guard = crate::DropGuard::from_option(drop_callback); Ok(Self { @@ -409,6 +423,7 @@ impl super::Instance { memory_budget_thresholds, debug_utils, get_physical_device_properties, + external_memory_capabilities, entry, has_nv_optimus, instance_api_version, diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 7a336a55083..ca64587e3f6 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -160,6 +160,7 @@ pub struct InstanceShared { memory_budget_thresholds: wgt::MemoryBudgetThresholds, debug_utils: Option, get_physical_device_properties: Option, + external_memory_capabilities: Option, entry: ash::Entry, has_nv_optimus: bool, android_sdk_version: u32, diff --git a/wgpu/src/api/device.rs b/wgpu/src/api/device.rs index 99ed5071df9..c64c7160ba0 100644 --- a/wgpu/src/api/device.rs +++ b/wgpu/src/api/device.rs @@ -349,6 +349,32 @@ impl Device { } } + /// # Safety + /// * TODO + #[must_use] + pub unsafe fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &BufferDescriptor<'_>, + ) -> Buffer { + let mut map_context = MapContext::new(); + if desc.mapped_at_creation { + map_context.initial_range = 0..desc.size; + } + + let buffer = self + .inner + .create_buffer_external_memory_fd(fd, offset, desc); + + Buffer { + inner: buffer, + map_context: Arc::new(Mutex::new(map_context)), + size: desc.size, + usage: desc.usage, + } + } + /// Creates a new [`Sampler`]. /// /// `desc` specifies the behavior of the sampler. diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index b44db4dba1f..f4c1bc31d67 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -1510,6 +1510,33 @@ impl dispatch::DeviceInterface for CoreDevice { .into() } + fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &crate::BufferDescriptor<'_>, + ) -> dispatch::DispatchBuffer { + let (id, error) = self.context.0.device_create_buffer_external_memory_fd( + self.id, + fd, + offset, + &desc.map_label(|l| l.map(Borrowed)), + None, + ); + + if let Some(cause) = error { + self.context + .handle_error(&self.error_sink, cause, desc.label, "Device::create_buffer"); + } + + CoreBuffer { + context: self.context.clone(), + id, + error_sink: Arc::clone(&self.error_sink), + } + .into() + } + fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> dispatch::DispatchTexture { let wgt_desc = desc.map_label_and_view_formats(|l| l.map(Borrowed), |v| v.to_vec()); let (id, error) = self diff --git a/wgpu/src/dispatch.rs b/wgpu/src/dispatch.rs index 558e48a1126..a92bb8b2b1d 100644 --- a/wgpu/src/dispatch.rs +++ b/wgpu/src/dispatch.rs @@ -156,6 +156,12 @@ pub trait DeviceInterface: CommonTraits { desc: &crate::PipelineCacheDescriptor<'_>, ) -> DispatchPipelineCache; fn create_buffer(&self, desc: &crate::BufferDescriptor<'_>) -> DispatchBuffer; + fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &crate::BufferDescriptor<'_>, + ) -> DispatchBuffer; fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> DispatchTexture; fn create_blas( &self, From f3d9e3e307a1c41485f78d1132c3e26deab206e9 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 16:41:57 -0500 Subject: [PATCH 04/16] Attempted to fix CI issues --- tests/tests/wgpu-gpu/external_memory.rs | 0 wgpu-hal/src/dx12/device.rs | 8 ++++++++ wgpu-hal/src/metal/device.rs | 8 ++++++++ wgpu/src/backend/webgpu.rs | 9 +++++++++ 4 files changed, 25 insertions(+) delete mode 100644 tests/tests/wgpu-gpu/external_memory.rs diff --git a/tests/tests/wgpu-gpu/external_memory.rs b/tests/tests/wgpu-gpu/external_memory.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index 918dd5193b9..953c0e0aac6 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -458,6 +458,14 @@ impl crate::Device for super::Device { allocation, }) } + unsafe fn create_buffer_external_memory_fd( + &self, + _fd: i32, + _offset: u64, + _desc: &crate::BufferDescriptor, + ) -> Result<::Buffer, crate::DeviceError> { + unreachable!() + } unsafe fn destroy_buffer(&self, buffer: super::Buffer) { suballocation::DeviceAllocationContext::from(self) diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index b74970e2bd9..a58dc773134 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -379,6 +379,14 @@ impl crate::Device for super::Device { }) }) } + unsafe fn create_buffer_external_memory_fd( + &self, + _fd: i32, + _offset: u64, + _desc: &crate::BufferDescriptor, + ) -> Result<::Buffer, crate::DeviceError> { + unreachable!() + } unsafe fn destroy_buffer(&self, _buffer: super::Buffer) { self.counters.buffers.sub(1); } diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 12662e7c010..68e36fc1818 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -2216,6 +2216,15 @@ impl dispatch::DeviceInterface for WebDevice { WebBuffer::new(self.inner.create_buffer(&mapped_desc).unwrap(), desc).into() } + fn create_buffer_external_memory_fd( + &self, + fd: i32, + offset: u64, + desc: &crate::BufferDescriptor<'_>, + ) -> DispatchBuffer { + panic!("VULKAN_EXTERNAL_MEMORY_FD feature must be enabled to call draw_mesh_tasks") + } + fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> dispatch::DispatchTexture { let mapped_desc = webgpu_sys::GpuTextureDescriptor::new( map_texture_format(desc.format), From 2b85b91a9c6b3f1ba4e6597aa74e65855d98b1a0 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 17:15:13 -0500 Subject: [PATCH 05/16] Hopefully fixed another issue --- tests/tests/wgpu-gpu/main.rs | 1 - wgpu-hal/src/vulkan/device.rs | 12 ++---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/tests/tests/wgpu-gpu/main.rs b/tests/tests/wgpu-gpu/main.rs index 9b02625d552..8fd24c43435 100644 --- a/tests/tests/wgpu-gpu/main.rs +++ b/tests/tests/wgpu-gpu/main.rs @@ -28,7 +28,6 @@ mod dispatch_workgroups_indirect; mod draw_indirect; mod dual_source_blending; mod encoder; -mod external_memory; mod external_texture; mod float32_filterable; mod image_atomics; diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 7367956723a..bce8c7ca490 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3117,16 +3117,8 @@ impl super::Device { self.counters.buffer_memory.add(desc.size as isize); self.counters.buffers.add(1); - Ok(super::Buffer { - raw, - block: Some(Mutex::new( - crate::vulkan::BufferMemoryBacking::VulkanMemory { - memory, - offset, - size: desc.size, - }, - )), - }) + // Block is none because wgpu doesn't actually own the memory + Ok(super::Buffer { raw, block: None }) } } From e64ad0a71df1fc9c2a76e2e3873dd36c6ade0ff5 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 18:16:14 -0500 Subject: [PATCH 06/16] Fixed webgpu compile error --- wgpu/src/backend/webgpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 68e36fc1818..33089db25e5 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -2221,7 +2221,7 @@ impl dispatch::DeviceInterface for WebDevice { fd: i32, offset: u64, desc: &crate::BufferDescriptor<'_>, - ) -> DispatchBuffer { + ) -> dispatch::DispatchBuffer { panic!("VULKAN_EXTERNAL_MEMORY_FD feature must be enabled to call draw_mesh_tasks") } From 6735a2844f3a4bc9ebb91dec331a87c1e77f5ea5 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 18:16:59 -0500 Subject: [PATCH 07/16] Fixed stupid typo --- wgpu-core/src/resource.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 8ffc0a32a20..3a98c5103ec 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -777,7 +777,9 @@ pub enum CreateBufferError { MissingDownlevelFlags(#[from] MissingDownlevelFlags), #[error("Failed to create bind group for indirect buffer validation: {0}")] IndirectValidationBindGroup(DeviceError), - #[error("Buffers created with `create_buffer_external_memory_fd` cannot be maped at creation")] + #[error( + "Buffers created with `create_buffer_external_memory_fd` cannot be mapped at creation" + )] ExternalMemoryMappedAtCreation, #[error("The VULKAN_EXTERNAL_MEMORY_FD feature must be enabled in order to create buffers from external memory file descriptors")] ExternalMemoryFeatureNotEnabled, From b46f40ee41a901ec5685c68cd86182cd5c5eda6f Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Wed, 18 Jun 2025 18:23:28 -0500 Subject: [PATCH 08/16] Attempted to fix another clippy lint --- wgpu/src/backend/webgpu.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 33089db25e5..b6470e4522d 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -2218,9 +2218,9 @@ impl dispatch::DeviceInterface for WebDevice { fn create_buffer_external_memory_fd( &self, - fd: i32, - offset: u64, - desc: &crate::BufferDescriptor<'_>, + _fd: i32, + _offset: u64, + _desc: &crate::BufferDescriptor<'_>, ) -> dispatch::DispatchBuffer { panic!("VULKAN_EXTERNAL_MEMORY_FD feature must be enabled to call draw_mesh_tasks") } From 7b9ec7bb73d1a5a7443105b33433504860d42cc8 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Fri, 20 Jun 2025 23:44:16 -0500 Subject: [PATCH 09/16] Fixed some stupid overlooked mistakes --- wgpu-hal/src/vulkan/device.rs | 15 ++++++++++++--- wgpu/src/backend/wgpu_core.rs | 8 ++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index bce8c7ca490..eb4d80b5c39 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3027,7 +3027,12 @@ impl super::Device { .as_ref() .unwrap(); let info = vk::PhysicalDeviceExternalBufferInfoKHR::default() - .flags(vk::BufferCreateFlags::empty()); + .usage(buffer_usage) + .handle_type(if is_win32 { + vk::ExternalMemoryHandleTypeFlagsKHR::OPAQUE_WIN32_KHR + } else { + vk::ExternalMemoryHandleTypeFlagsKHR::OPAQUE_FD_KHR + }); (caps.fp().get_physical_device_external_buffer_properties_khr)( self.raw_physical_device(), &info, @@ -3086,7 +3091,9 @@ impl super::Device { let mut import_info = vk::ImportMemoryWin32HandleInfoKHR::default() .handle(handle as isize) .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32_KHR); - let mut allocate_info = vk::MemoryAllocateInfo::default().push_next(&mut import_info); + let mut allocate_info = vk::MemoryAllocateInfo::default() + .allocation_size(desc.size) + .push_next(&mut import_info); if needs_dedicated { allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); } @@ -3095,7 +3102,9 @@ impl super::Device { let mut import_info = vk::ImportMemoryFdInfoKHR::default() .fd(handle as i32) .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD_KHR); - let mut allocate_info = vk::MemoryAllocateInfo::default().push_next(&mut import_info); + let mut allocate_info = vk::MemoryAllocateInfo::default() + .allocation_size(desc.size) + .push_next(&mut import_info); if needs_dedicated { allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); } diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index f4c1bc31d67..795d8f1e8ad 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -1525,8 +1525,12 @@ impl dispatch::DeviceInterface for CoreDevice { ); if let Some(cause) = error { - self.context - .handle_error(&self.error_sink, cause, desc.label, "Device::create_buffer"); + self.context.handle_error( + &self.error_sink, + cause, + desc.label, + "Device::create_buffer_external_memory_fd", + ); } CoreBuffer { From dc8f669853edc3be3381b1323e8ac60948af639a Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Sat, 21 Jun 2025 00:09:40 -0500 Subject: [PATCH 10/16] Made imported memory not zero initialized --- wgpu-core/src/device/resource.rs | 42 ++++---------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 6550c6a6f9c..f192bcb2065 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -905,15 +905,14 @@ impl Device { let indirect_validation_bind_groups = self.create_indirect_validation_bind_groups(buffer.as_ref(), desc.size, desc.usage)?; + let mut init_tracker = BufferInitTracker::new(aligned_size); + init_tracker.drain(0..aligned_size); let buffer = Buffer { raw: Snatchable::new(buffer), device: self.clone(), usage: desc.usage, size: desc.size, - initialization_status: RwLock::new( - rank::BUFFER_INITIALIZATION_STATUS, - BufferInitTracker::new(aligned_size), - ), + initialization_status: RwLock::new(rank::BUFFER_INITIALIZATION_STATUS, init_tracker), map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle), label: desc.label.to_string(), tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()), @@ -924,43 +923,12 @@ impl Device { let buffer = Arc::new(buffer); - let buffer_use = if !desc.mapped_at_creation { - wgt::BufferUses::empty() - } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) { - // buffer is mappable, so we are just doing that at start - let map_size = buffer.size; - let mapping = if map_size == 0 { - hal::BufferMapping { - ptr: core::ptr::NonNull::dangling(), - is_coherent: true, - } - } else { - let snatch_guard: SnatchGuard = self.snatchable_lock.read(); - map_buffer(&buffer, 0, map_size, HostMap::Write, &snatch_guard)? - }; - *buffer.map_state.lock() = resource::BufferMapState::Active { - mapping, - range: 0..map_size, - host: HostMap::Write, - }; - wgt::BufferUses::MAP_WRITE - } else { - let mut staging_buffer = - StagingBuffer::new(self, wgt::BufferSize::new(aligned_size).unwrap())?; - - // Zero initialize memory and then mark the buffer as initialized - // (it's guaranteed that this is the case by the time the buffer is usable) - staging_buffer.write_zeros(); - buffer.initialization_status.write().drain(0..aligned_size); - - *buffer.map_state.lock() = resource::BufferMapState::Init { staging_buffer }; - wgt::BufferUses::COPY_DST - }; + // Don't zero initialize the memory self.trackers .lock() .buffers - .insert_single(&buffer, buffer_use); + .insert_single(&buffer, wgt::BufferUses::empty()); Ok(buffer) } From 4acb314cbf5c2e9b9cbb65d2511e81254aa74fff Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Sat, 21 Jun 2025 15:41:14 -0500 Subject: [PATCH 11/16] Tried to fix another bug --- wgpu-hal/src/vulkan/device.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index eb4d80b5c39..6ac91231b9e 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3019,6 +3019,12 @@ impl super::Device { ) -> Result<<::A as crate::Api>::Buffer, crate::DeviceError> { let buffer_usage = conv::map_buffer_usage(desc.usage); + let handle_type = if is_win32 { + vk::ExternalMemoryHandleTypeFlagsKHR::OPAQUE_WIN32_KHR + } else { + vk::ExternalMemoryHandleTypeFlagsKHR::OPAQUE_FD_KHR + }; + let mut external_properties = vk::ExternalBufferProperties::default(); unsafe { let caps = self @@ -3028,11 +3034,7 @@ impl super::Device { .unwrap(); let info = vk::PhysicalDeviceExternalBufferInfoKHR::default() .usage(buffer_usage) - .handle_type(if is_win32 { - vk::ExternalMemoryHandleTypeFlagsKHR::OPAQUE_WIN32_KHR - } else { - vk::ExternalMemoryHandleTypeFlagsKHR::OPAQUE_FD_KHR - }); + .handle_type(handle_type); (caps.fp().get_physical_device_external_buffer_properties_khr)( self.raw_physical_device(), &info, @@ -3051,10 +3053,13 @@ impl super::Device { .external_memory_features .contains(vk::ExternalMemoryFeatureFlags::DEDICATED_ONLY_KHR); + let mut external_vk_info = + vk::ExternalMemoryBufferCreateInfoKHR::default().handle_types(handle_type); let vk_info = vk::BufferCreateInfo::default() .size(desc.size) .usage(buffer_usage) - .sharing_mode(vk::SharingMode::EXCLUSIVE); + .sharing_mode(vk::SharingMode::EXCLUSIVE) + .push_next(&mut external_vk_info); let raw = unsafe { self.shared From 1bf5ad1aa0b562de5990e5b3f768fd518b04aa2a Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Sat, 21 Jun 2025 15:47:39 -0500 Subject: [PATCH 12/16] Tried to fix more issues --- wgpu-hal/src/vulkan/device.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 6ac91231b9e..b70434c9ef6 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3090,6 +3090,7 @@ impl super::Device { gpu_alloc::UsageFlags::TRANSIENT, desc.memory_flags.contains(crate::MemoryFlags::TRANSIENT), ); + let reqs = unsafe { self.raw_device().get_buffer_memory_requirements(raw) }; let mut dedicated_alloc_info = vk::MemoryDedicatedAllocateInfoKHR::default().buffer(raw); let memory = if is_win32 { @@ -3097,7 +3098,8 @@ impl super::Device { .handle(handle as isize) .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32_KHR); let mut allocate_info = vk::MemoryAllocateInfo::default() - .allocation_size(desc.size) + .allocation_size(reqs.size) + .memory_type_index(reqs.memory_type_bits.leading_zeros()) .push_next(&mut import_info); if needs_dedicated { allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); @@ -3108,7 +3110,8 @@ impl super::Device { .fd(handle as i32) .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD_KHR); let mut allocate_info = vk::MemoryAllocateInfo::default() - .allocation_size(desc.size) + .allocation_size(reqs.size) + .memory_type_index(reqs.memory_type_bits.leading_zeros()) .push_next(&mut import_info); if needs_dedicated { allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); From 5957a7ea9b37f2fe410e4e2f364eb9747a706fb4 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Sat, 21 Jun 2025 15:48:48 -0500 Subject: [PATCH 13/16] Oops --- wgpu-hal/src/vulkan/device.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index b70434c9ef6..32c56af48bf 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3099,7 +3099,7 @@ impl super::Device { .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_WIN32_KHR); let mut allocate_info = vk::MemoryAllocateInfo::default() .allocation_size(reqs.size) - .memory_type_index(reqs.memory_type_bits.leading_zeros()) + .memory_type_index(reqs.memory_type_bits.trailing_zeros()) .push_next(&mut import_info); if needs_dedicated { allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); @@ -3111,7 +3111,7 @@ impl super::Device { .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD_KHR); let mut allocate_info = vk::MemoryAllocateInfo::default() .allocation_size(reqs.size) - .memory_type_index(reqs.memory_type_bits.leading_zeros()) + .memory_type_index(reqs.memory_type_bits.trailing_zeros()) .push_next(&mut import_info); if needs_dedicated { allocate_info = allocate_info.push_next(&mut dedicated_alloc_info); From 86f6f4574d8bd6ddc935fa08dc8da63f2b2d23ce Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Sat, 21 Jun 2025 15:55:29 -0500 Subject: [PATCH 14/16] Made external memory freed on buffer destroy --- wgpu-hal/src/vulkan/device.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 32c56af48bf..e77e8d01cf2 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3135,7 +3135,14 @@ impl super::Device { self.counters.buffers.add(1); // Block is none because wgpu doesn't actually own the memory - Ok(super::Buffer { raw, block: None }) + Ok(super::Buffer { + raw, + block: Some(Mutex::new(super::BufferMemoryBacking::VulkanMemory { + memory, + offset, + size: reqs.size, + })), + }) } } From 565347c14d034649a2cbde9507d43b218358cb16 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Sun, 22 Jun 2025 17:07:17 -0500 Subject: [PATCH 15/16] Made external memory use CONCURRENT sharing mode --- wgpu-hal/src/vulkan/device.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index e77e8d01cf2..5361e2733a7 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3058,7 +3058,7 @@ impl super::Device { let vk_info = vk::BufferCreateInfo::default() .size(desc.size) .usage(buffer_usage) - .sharing_mode(vk::SharingMode::EXCLUSIVE) + .sharing_mode(vk::SharingMode::CONCURRENT) .push_next(&mut external_vk_info); let raw = unsafe { @@ -3134,7 +3134,6 @@ impl super::Device { self.counters.buffer_memory.add(desc.size as isize); self.counters.buffers.add(1); - // Block is none because wgpu doesn't actually own the memory Ok(super::Buffer { raw, block: Some(Mutex::new(super::BufferMemoryBacking::VulkanMemory { From 5b75c083acabc83b19e067c31011be4fa6e1f174 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Fri, 27 Jun 2025 19:54:03 -0500 Subject: [PATCH 16/16] Made it have exclusive access again --- wgpu-hal/src/vulkan/device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 5361e2733a7..95763195512 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -3058,7 +3058,7 @@ impl super::Device { let vk_info = vk::BufferCreateInfo::default() .size(desc.size) .usage(buffer_usage) - .sharing_mode(vk::SharingMode::CONCURRENT) + .sharing_mode(vk::SharingMode::EXCLUSIVE) .push_next(&mut external_vk_info); let raw = unsafe {