Making use of Metal's volatile MTLPurgeableState while triple buffering

I have a number of cached MTLTexture's which I would like to set to purgeable using MTLTexture's setPurgeableState API. In order to do this I follow the process outlined in Apple's 2019 WWDC video which suggests:

  • Setting cached MTLTexture instances to volatile
  • Flagging MTLTexture instances as nonVolatile while 'in use' as part of a command buffer
  • Using MTLCommandBuffer's addCompletedHandler to set all MTLTexture instances back to volatile after the command buffer completes its work

This approach works great, but quickly runs into issues in a triple buffered renderer where more than one command buffer is in-flight simultaneously. In these instances I receive the following error:

Cannot set purgeability state to volatile while resource is in use by a command buffer.

... which makes sense. I'm obviously attempting to flag a MTLTexture as volatile while it's in-flight as part of a subsequent command buffer. But what's the best way around this without obliterating the performance advantages afforded by triple buffering in the first place?

We don't recommend that you set the purgeability state that frequently (like in a per frame bases as you're attempting to do). Setting the purgeability state to volatile is most useful if your app has a resource that it knows it will not use for some time.

For instance, if you're implementing an open world game and your player is in a snowy mountainous portion of the map, maybe you'd set all the the textures for cacti as purgeable since the character isn't likely to see a cactus anytime soon. If the player moves to a desert portion, you can check if the OS has reclaimed the cactus texture's memory and, if so, reload it from disk.

Setting one of your textures in a triple buffered scheme to volatile would be pretty expensive as Metal needs to indicates to the OS that it can reclaim the memory. Additionally, you wouldn't really be saving any memory since you'd want it back almost immediately when you set it to non-volatile in the next 3 frames.

Making use of Metal's volatile MTLPurgeableState while triple buffering
 
 
Q