Documentation Archive


Metal Best Practices Guide

On This Page

Persistent Objects

Best Practice: Create persistent objects early and reuse them often.

The Metal framework provides protocols to manage persistent objects throughout the lifetime of your app. These objects are expensive to create but are usually initialized once and reused often. You do not need to create these objects at the beginning of every render or compute loop.

Initialize Your Device and Command Queue First

Call the MTLCreateSystemDefaultDevice function at the start of your app to obtain the default system device. Next, call the newCommandQueue or newCommandQueueWithMaxCommandBufferCount: method to create a command queue for executing GPU instructions on that device.

All apps should create only one MTLDevice object per GPU and reuse it for all your Metal work on that GPU. Most apps should create only one MTLCommandQueue object per GPU, though you may want more if each command queue represents different Metal work (for example, non-real-time compute processing and real-time graphics rendering).

Compile Your Functions and Build Your Library at Build Time

For an overview of compiling your functions and building your library at build time, see the Functions and Libraries best practices.

At runtime, use the MTLLibrary and MTLFunction objects to access your library of graphics and compute functions. Avoid building your library at runtime or fetching functions during a render or compute loop.

If you need to configure multiple render or compute pipelines, reuse MTLFunction objects whenever possible. You can release MTLLibrary and MTLFunction objects after building all render and compute pipelines that depend on them.

Build Your Pipelines Once and Reuse Them Often

Building a programmable pipeline involves an expensive evaluation of GPU state. You should build MTLRenderPipelineState and MTLComputePipelineState objects only once, then reuse them for every new render or compute command encoder you create. Do not build new pipelines for new command encoders. For an overview of building multiple pipelines asynchronously, see the Pipelines best practices.

Allocate Resource Storage Up Front

Resource data may be static or dynamic and accessed at various stages throughout the lifetime of your app. However, the MTLBuffer and MTLTexture objects that allocate memory for this data should be created as early as possible. After these objects are created, the resource properties and storage allocation are immutable, but the data itself is not; you can update the data whenever necessary.

Reuse MTLBuffer and MTLTexture objects as much as possible, particularly for static data. Avoid creating new resources during a render or compute loop, even for dynamic data. For further information about buffers and textures, see the Resource Management and Triple Buffering best practices.