Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions wgpu-hal/src/metal/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ impl crate::CommandEncoder for super::CommandEncoder {

self.raw_cmd_buf = Some(raw);

// Clear resource tracking for new command buffer
self.used_buffers.clear();
self.used_textures.clear();

Ok(())
}

Expand All @@ -346,6 +350,10 @@ impl crate::CommandEncoder for super::CommandEncoder {
encoder.end_encoding();
}
self.raw_cmd_buf = None;

// Clear resource tracking since we're discarding
self.used_buffers.clear();
self.used_textures.clear();
}

unsafe fn end_encoding(&mut self) -> Result<super::CommandBuffer, crate::DeviceError> {
Expand All @@ -362,6 +370,9 @@ impl crate::CommandEncoder for super::CommandEncoder {

Ok(super::CommandBuffer {
raw: self.raw_cmd_buf.take().unwrap(),
// Transfer resource references to keep them alive until GPU completion
used_buffers: core::mem::take(&mut self.used_buffers),
used_textures: core::mem::take(&mut self.used_textures),
})
}

Expand All @@ -387,6 +398,9 @@ impl crate::CommandEncoder for super::CommandEncoder {
unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) {
let encoder = self.enter_blit();
encoder.fill_buffer(&buffer.raw, conv::map_range(&range), 0);

// Retain buffer reference until command buffer completes
self.used_buffers.push(buffer.raw.clone());
}

unsafe fn copy_buffer_to_buffer<T>(
Expand All @@ -407,6 +421,10 @@ impl crate::CommandEncoder for super::CommandEncoder {
copy.size.get(),
);
}

// Retain buffer references until command buffer completes
self.used_buffers.push(src.raw.clone());
self.used_buffers.push(dst.raw.clone());
}

unsafe fn copy_texture_to_texture<T>(
Expand Down Expand Up @@ -444,6 +462,10 @@ impl crate::CommandEncoder for super::CommandEncoder {
dst_origin,
);
}

// Retain texture references until command buffer completes
self.used_textures.push(src.raw.clone());
self.used_textures.push(dst.raw.clone());
}

unsafe fn copy_buffer_to_texture<T>(
Expand Down Expand Up @@ -486,6 +508,10 @@ impl crate::CommandEncoder for super::CommandEncoder {
conv::get_blit_option(dst.format, copy.texture_base.aspect),
);
}

// Retain resource references until command buffer completes
self.used_buffers.push(src.raw.clone());
self.used_textures.push(dst.raw.clone());
}

unsafe fn copy_texture_to_buffer<T>(
Expand Down Expand Up @@ -523,6 +549,10 @@ impl crate::CommandEncoder for super::CommandEncoder {
conv::get_blit_option(src.format, copy.texture_base.aspect),
);
}

// Retain resource references until command buffer completes
self.used_textures.push(src.raw.clone());
self.used_buffers.push(dst.raw.clone());
}

unsafe fn copy_acceleration_structure_to_acceleration_structure(
Expand Down Expand Up @@ -822,6 +852,17 @@ impl crate::CommandEncoder for super::CommandEncoder {
self.state.render = Some(encoder.to_owned());
});

// Retain texture references for render attachments until command buffer completes
for at in desc.color_attachments.iter().flatten() {
self.used_textures.push(at.target.view.raw.clone());
if let Some(ref resolve) = at.resolve_target {
self.used_textures.push(resolve.view.raw.clone());
}
}
if let Some(ref at) = desc.depth_stencil_attachment {
self.used_textures.push(at.target.view.raw.clone());
}

Ok(())
}

Expand Down Expand Up @@ -1127,6 +1168,9 @@ impl crate::CommandEncoder for super::CommandEncoder {
stride,
raw_type,
});

// Retain buffer reference until command buffer completes
self.used_buffers.push(binding.buffer.raw.clone());
}

unsafe fn set_vertex_buffer<'a>(
Expand Down Expand Up @@ -1158,6 +1202,9 @@ impl crate::CommandEncoder for super::CommandEncoder {
sizes.as_ptr().cast(),
);
}

// Retain buffer reference until command buffer completes
self.used_buffers.push(binding.buffer.raw.clone());
}

unsafe fn set_viewport(&mut self, rect: &crate::Rect<f32>, depth_range: Range<f32>) {
Expand Down Expand Up @@ -1300,6 +1347,9 @@ impl crate::CommandEncoder for super::CommandEncoder {
encoder.draw_primitives_indirect(self.state.raw_primitive_type, &buffer.raw, offset);
offset += size_of::<wgt::DrawIndirectArgs>() as wgt::BufferAddress;
}

// Retain indirect buffer reference until command buffer completes
self.used_buffers.push(buffer.raw.clone());
}

unsafe fn draw_indexed_indirect(
Expand All @@ -1321,6 +1371,9 @@ impl crate::CommandEncoder for super::CommandEncoder {
);
offset += size_of::<wgt::DrawIndexedIndirectArgs>() as wgt::BufferAddress;
}

// Retain indirect buffer reference until command buffer completes
self.used_buffers.push(buffer.raw.clone());
}

unsafe fn draw_mesh_tasks_indirect(
Expand Down Expand Up @@ -1505,6 +1558,9 @@ impl crate::CommandEncoder for super::CommandEncoder {
offset,
self.state.stage_infos.cs.raw_wg_size,
);

// Retain indirect buffer reference until command buffer completes
self.used_buffers.push(buffer.raw.clone());
}

unsafe fn build_acceleration_structures<'a, T>(
Expand Down
2 changes: 2 additions & 0 deletions wgpu-hal/src/metal/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,8 @@ impl crate::Device for super::Device {
state: super::CommandState::default(),
temp: super::Temp::default(),
counters: Arc::clone(&self.counters),
used_buffers: Vec::new(),
used_textures: Vec::new(),
})
}

Expand Down
25 changes: 16 additions & 9 deletions wgpu-hal/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,19 +323,11 @@ struct PrivateDisabilities {
broken_layered_clear_image: bool,
}

#[derive(Debug)]
#[derive(Debug, Default)]
struct Settings {
retain_command_buffer_references: bool,
}

impl Default for Settings {
fn default() -> Self {
Self {
retain_command_buffer_references: true,
}
}
}

struct AdapterShared {
device: Mutex<metal::Device>,
disabilities: PrivateDisabilities,
Expand Down Expand Up @@ -1022,6 +1014,11 @@ pub struct CommandEncoder {
state: CommandState,
temp: Temp,
counters: Arc<wgt::HalCounters>,
/// Buffers used during encoding of the current command buffer.
/// These are transferred to the CommandBuffer in end_encoding().
used_buffers: Vec<metal::Buffer>,
/// Textures used during encoding of the current command buffer.
used_textures: Vec<metal::Texture>,
}

impl fmt::Debug for CommandEncoder {
Expand All @@ -1039,6 +1036,16 @@ unsafe impl Sync for CommandEncoder {}
#[derive(Debug)]
pub struct CommandBuffer {
raw: metal::CommandBuffer,
/// Metal buffer handles used by this command buffer.
///
/// When `retain_command_buffer_references` is false, Metal's command buffer
/// doesn't automatically retain resources. We keep these handles alive
/// until the command buffer completes execution to prevent use-after-free.
#[expect(dead_code, reason = "Keeps strong references to resources")]
used_buffers: Vec<metal::Buffer>,
/// Metal texture handles used by this command buffer.
#[expect(dead_code, reason = "Keeps strong references to resources")]
used_textures: Vec<metal::Texture>,
}

impl crate::DynCommandBuffer for CommandBuffer {}
Expand Down
Loading