Metal object is being destroyed while required by command buffer

Hi, I work on a app with Metal-cpp rendering. The app does not use an MTKView. Instead, it requests a drawable from the MetalLayer and renders to the drawable's texture.

When calling [CAMetalLayer nextDrawable] i sometime get this error:

-[MTLDebugDevice notifyExternalReferencesNonZeroOnDealloc:]:2885: failed assertion `The following Metal object is being destroyed while still required to be alive by the command buffer 0x142944a00 (label: <no label set>):
<MTLToolsObject: 0x16e22b0a0> -> <AGXG13GFamilyTexture: 0x16e232a80>
    label = CAMetalLayer Display Drawable
    textureType = MTLTextureType2D

The call stack shows that nextDrawable tries to delete some textures.

1- I am confused by this message. Can someone confirm which object "is being destroyed"? The AGXG13GFamilyTexture or the MTLToolsObject?

2- I did a memory graph but i see no direct or indirect link between the texture and the command buffer. How can I find who is preventing the object from being destroyed?

My workstation: M1 Mac mini, MacOS Ventura 13.0.1, XCode 14.1

Answered by Graphics and Games Engineer in 740058022

1- I am confused by this message. Can someone confirm which object "is being destroyed"? The AGXG13GFamilyTexture or the MTLToolsObject?

They are the same object. The MTLToolsObject is a class used when Metal API validation is enabled and it wrapping the actual driver object (AGXG13GFamilyTexture) which represents the object as used by the GPU.

2- I did a memory graph but i see no direct or indirect link between the texture and the command buffer. How can I find who is preventing the object from being destroyed?

A mengraph may not show you the relation between all objects. But it looks like CA is trying to destroy a drawable texture before a command buffer drawing to that texture has not completed.

A few things that would be useful to know. Are you creating command buffers with -[MTLCommandQueue commandBufferWithUnretainedReferences]? Does this occur after/during a resize of the window? Are you double or triple buffered?

Something you can try is taking a strong reference to each drawable and only releasing it until the command buffer drawing to it has been presented. Something like the following:

id<MTLTexture> drawableTexture = [drawable.texture retain];
[myCmdBuf addCompletedHandler:^(...)
{
    [drawableTexture release];
}];
Accepted Answer

1- I am confused by this message. Can someone confirm which object "is being destroyed"? The AGXG13GFamilyTexture or the MTLToolsObject?

They are the same object. The MTLToolsObject is a class used when Metal API validation is enabled and it wrapping the actual driver object (AGXG13GFamilyTexture) which represents the object as used by the GPU.

2- I did a memory graph but i see no direct or indirect link between the texture and the command buffer. How can I find who is preventing the object from being destroyed?

A mengraph may not show you the relation between all objects. But it looks like CA is trying to destroy a drawable texture before a command buffer drawing to that texture has not completed.

A few things that would be useful to know. Are you creating command buffers with -[MTLCommandQueue commandBufferWithUnretainedReferences]? Does this occur after/during a resize of the window? Are you double or triple buffered?

Something you can try is taking a strong reference to each drawable and only releasing it until the command buffer drawing to it has been presented. Something like the following:

id<MTLTexture> drawableTexture = [drawable.texture retain];
[myCmdBuf addCompletedHandler:^(...)
{
    [drawableTexture release];
}];

Retain and then release the drawable in a handler fixed the issue. Thank you so much!

Are you creating command buffers with -[MTLCommandQueue commandBufferWithUnretainedReferences]

Yes we do.

Does this occur after/during a resize of the window?

It occurs at launch. I cannot confirm it happens on a resize.

Are you double or triple buffered?

Triple.

Metal object is being destroyed while required by command buffer
 
 
Q