Skip to content

Conversation

@jeffcrouse
Copy link
Contributor

@jeffcrouse jeffcrouse commented Dec 22, 2025

Summary

The Drop implementation for WGPUCommandEncoderImpl only ran cleanup when self.open was true, but wgpuCommandEncoderFinish set open=false before the encoder was released. This meant command_encoder_drop() was never called, causing memory to leak.

The Bug

// In wgpuCommandEncoderFinish (line 1401):
command_encoder.open.store(false, atomic::Ordering::SeqCst);

// In Drop (line 129):
if self.open.load(atomic::Ordering::SeqCst) && !thread::panicking() {
    context.command_encoder_drop(self.id);  // Never reached!
}

The Fix

Remove the open check from Drop - cleanup should always run when the encoder is released:

if !thread::panicking() {
    context.command_encoder_drop(self.id);
}

Test Results

Version Memory at 0s Memory at 180s Behavior
Before fix 23 MB 33 MB +0.5 MB every 10 seconds
After fix 21 MB 21 MB Stable

Reproduction

Minimal C++ test that creates command encoders at 60fps:

while (true) {
    WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
    WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &passDesc);
    wgpuRenderPassEncoderEnd(pass);
    wgpuRenderPassEncoderRelease(pass);
    WGPUCommandBuffer cmdBuf = wgpuCommandEncoderFinish(encoder, nullptr);
    wgpuQueueSubmit(queue, 1, &cmdBuf);
    wgpuCommandBufferRelease(cmdBuf);
    wgpuCommandEncoderRelease(encoder);
    std::this_thread::sleep_for(std::chrono::milliseconds(16));
}

Fixes #541
Fixes #527

Notes

  • The open flag still serves a purpose in wgpuCommandEncoderFinish to prevent double-finishing
  • This fix ensures cleanup happens regardless of whether finish() was called
  • Tested on macOS Sequoia / Apple M4 Max with wgpu-native v27

The Drop implementation for WGPUCommandEncoderImpl only ran cleanup when
self.open was true, but wgpuCommandEncoderFinish set open=false before
the encoder was released. This meant command_encoder_drop() was never
called, causing memory to leak (~0.5 MB per 10 seconds at 60fps).

The fix removes the open check from Drop - cleanup should always run
when the encoder is released, whether it was finished or not.

Fixes gfx-rs#541
Copy link
Member

@cwfitzgerald cwfitzgerald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@cwfitzgerald cwfitzgerald enabled auto-merge (squash) December 22, 2025 20:29
@cwfitzgerald cwfitzgerald merged commit c3edf78 into gfx-rs:trunk Dec 22, 2025
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Command encoder memory leak on Metal (and all backends) - cleanup never runs Memory leak from command encoder on Windows & macOS

2 participants