Memory leak when no draw calls issued to encoder

I noticed that when the render command encoder adds no draw calls an apps memory usage seems to grow unboundedly. Using a super simple MTKView-based drawing with the following delegate (code at end).

If I add the simplest of draw calls, e.g., a single vertex, the app's memory usage is normal, around 100-ish MBs.

I am attaching a couple screenshot, one from Xcode and one from Instruments.

What's going on here? Is this an illegal program? If yes, why does it not crash, such as if the encode or command buffer weren't ended.

Or is there some race condition at play here due to the lack of draws?

class Renderer: NSObject, MTKViewDelegate {
  var device: MTLDevice
  var commandQueue: MTL4CommandQueue
  var commandBuffer: MTL4CommandBuffer
  var allocator: MTL4CommandAllocator

  override init() {
    guard let d = MTLCreateSystemDefaultDevice(),
      let queue = d.makeMTL4CommandQueue(),
      let cmdBuffer = d.makeCommandBuffer(),
      let alloc = d.makeCommandAllocator()
    else {
      fatalError("unable to create metal 4 objects")
    }
    self.device = d
    self.commandQueue = queue
    self.commandBuffer = cmdBuffer
    self.allocator = alloc
    super.init()
  }

  func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}

  func draw(in view: MTKView) {
    guard let drawable = view.currentDrawable else { return }
    commandBuffer.beginCommandBuffer(allocator: allocator)
    guard let descriptor = view.currentMTL4RenderPassDescriptor,
      let encoder = commandBuffer.makeRenderCommandEncoder(
        descriptor: descriptor
      )
    else {
      fatalError("unable to create encoder")
    }
    encoder.endEncoding()
    commandBuffer.endCommandBuffer()
    commandQueue.waitForDrawable(drawable)
    commandQueue.commit([commandBuffer])
    commandQueue.signalDrawable(drawable)
    drawable.present()
  }
}
Answered by g_i_i_k in 871971022

It appears the memory consumption was due to the Shader and API Validation I had enabled in my scheme. Once I disabled these, memory use went back down to normal.

Hm that's weird! The only thing that sticks out for me is that you're using the new MTL4CommandAllocator API.

I see they have a reset instance method which can be called manually to free memory from command buffers. Since you are not encoding anything, perhaps the internal logic that would automatically free the memory doesn't trigger?

That's my guess, worth adding a reset at the end to see if it fixes it. Still sounds like a bug so consider reporting it!

I'm curious, what's the usecase for not encoding anything during a pass?

That's my guess, worth adding a reset at the end to see if it fixes it. Still sounds like a bug so consider reporting it!

Good point. Tried reset()-ing but didn't do much. Also, printing the allocator's allocatedSize() shows constant ~2.5MiB

There was no point really in doing this, it was mostly me playing with the Metal 4 API. If this is a legal program it should not leak is my position.

It has to be the buffer. This version works and does not leak:

  func draw(in view: MTKView) {
    guard let drawable = view.currentDrawable else { return }
    commandQueue.waitForDrawable(drawable)
    commandQueue.signalDrawable(drawable)
    drawable.present()
    print(self.allocator.allocatedSize())
    self.allocator.reset();
  }

but this version of draw does:

  func draw(in view: MTKView) {
    guard let drawable = view.currentDrawable else { return }
    commandBuffer.beginCommandBuffer(allocator: allocator)
    commandBuffer.endCommandBuffer()
    commandQueue.waitForDrawable(drawable)
    commandQueue.commit([commandBuffer])
    commandQueue.signalDrawable(drawable)
    drawable.present()
    print(self.allocator.allocatedSize())
    self.allocator.reset();
  }```

Both of those aren't fully working though, as the view does not display its clear color.

Something's not working right, I think. I'll file a bug.
Accepted Answer

It appears the memory consumption was due to the Shader and API Validation I had enabled in my scheme. Once I disabled these, memory use went back down to normal.

Memory leak when no draw calls issued to encoder
 
 
Q