Traverse-Research / gpu-allocator

🦀 GPU memory allocator for Vulkan, DirectX 12 and Metal. Written in pure Rust
https://traverse.nl/
Apache License 2.0
380 stars 50 forks source link

memory leak inside gpu-allocator #127

Closed Makogan closed 2 years ago

Makogan commented 2 years ago

OS: ubuntu 22.04 GPU: Nivida Gtx 1070 API: Vulkan, through ash bindings gpu-allocator version: "0.18.0"

It is hard for me to say with 100% confidence, but I think there might be a bug inside gpu-allocator causing memleaks.

I have a vulkan program which is allocating and releasing resources in a loop. Said loop looks like this:

pub fn draw<RenderContext>(&mut self, render_context : &mut RenderContext)
where RenderContext : GpuResourceCreation
{
    let clipped_primitives = self.ctx.tessellate(output.shapes);
    for primitive in clipped_primitives
    {
        let (vbuffer, ibuffer, icount, _) = match primitive.primitive {
        Primitive::Mesh(mesh) => {
                let vbuffer = render_context.allocate_buffer(
                    mesh.vertices.as_ptr() as _,
                    mesh.vertices.len() * size_of::<epaint::Vertex>());

                let ibuffer = render_context.allocate_buffer(
                    mesh.indices.as_ptr() as _,
                    mesh.indices.len() * size_of::<u32>());

                let tex_id = mesh.texture_id;

                (vbuffer, ibuffer, mesh.indices.len(), tex_id)
            }
            _ => todo!()
        };

        render_context.destroy_buffer(&vbuffer);
        render_context.destroy_buffer(&ibuffer);
    }
 }

Where:

 fn destroy_buffer(&mut self, buffer : &BufferHandle)
{
    let index = self.buffers.iter().position(|x| x.as_raw() == buffer.to_raw()).unwrap();
    self.buffers.remove(index);
    self.memory.destroy_buffer(vk::Buffer::from_raw(buffer.to_raw()));
}

pub(crate) fn destroy_buffer(&mut self, buffer : vk::Buffer)
{
    debug_assert!(self.buffer_allocations.contains_key(&buffer.as_raw()));

    let buffer_allocation =
        self.buffer_allocations.remove(&buffer.as_raw()).unwrap();
    self.vk_allocator.free(buffer_allocation)
        .expect("Failed to free vulkan memory allocation.");
    unsafe {
        self.hardware_interface()
            .device.destroy_buffer(buffer, None);
    }
}

I know for a fact this is the only place in the entire program that can allocate memory repeatedly. I am noticing that over time (20 minutes) my program increasingly becomes slower until it eventually crashes. Renderdoc seems to suggest I run out of device host memory. I am noticing, using nvidia-smi that my program consumes progressively more vram from 325 MiB to 327 MiB it's a small amount but it;s very conistent.

If I comment out that loop the rest of the program renders fine, never gets slower and renderdoc never complains about memory running out.

This code is alsoa port from a C++ codebase that uses VMA and the error does not seem to happen there. All of the above leads me to suspect there might be an issue with gpu-allocator.

Makogan commented 2 years ago

Nvm this was user error, ash does not auto free fences when they go out of scope, it;s unrelated to gpu allocator, apologies for the disturbance.