Article

Copying Data to a Private Resource

Use a blit command encoder to copy buffer or texture data to a private resource.

Overview

Resources with a MTLStorageMode.private storage mode are accessible only to the GPU. Private resources perform better than shared resources, and you don’t have to explicitly synchronize them the way you do for managed resources.

However, because private resources aren’t accessible to the CPU, you can’t populate them with it. To write data from the CPU to a private resource, you must first write the data to a shared or managed resource. You can then copy the data from that resource to the private resource.

For more information about resource storage modes, see Setting Resource Storage Modes.

Copying Data from a Shared Buffer to a Private Buffer

First, create a shared buffer and populate its contents.

// Create and populate a source buffer.
id <MTLBuffer> _sourceBuffer;
_sourceBuffer = [_device newBufferWithBytes:bufferData
                                     length:bufferLength
                                    options:MTLResourceStorageModeShared];

Next, create a private buffer that’s large enough to store your buffer data.

// Create a private buffer.
id <MTLBuffer> _privateBuffer;
_privateBuffer = [_device newBufferWithLength:bufferLength
                                      options:MTLResourceStorageModePrivate];

Finally, encode and commit a copy(from:sourceOffset:to:destinationOffset:size:) command. Set the shared buffer as the sourceBuffer parameter. Set the private buffer as the destinationBuffer parameter.

// Create a command buffer for GPU work.
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

// Encode a blit pass to copy data from the source buffer to the private buffer.
id <MTLBlitCommandEncoder> blitCommandEncoder = [commandBuffer blitCommandEncoder];
[blitCommandEncoder copyFromBuffer:_sourceBuffer
                      sourceOffset:0
                          toBuffer:_privateBuffer
                 destinationOffset:0 size:bufferLength];
[blitCommandEncoder endEncoding];

// Add a completion handler and commit the command buffer.
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
    // Private buffer is populated.
}];
[commandBuffer commit];

Copying Data from a Shared Buffer to a Private Texture

Use this implementation to copy texture data from the CPU to a private texture, without having to synchronize a managed texture.

First, create a shared buffer and populate its contents with your texture data.

// Create and populate a source buffer with texture data.
id <MTLBuffer> _sourceBuffer;
_sourceBuffer = [_device newBufferWithBytes:textureData
                                     length:pixelSize*textureSize.width*textureSize.height
                                    options:MTLResourceStorageModeShared];

Next, create a private texture with a suitable configuration for the texture data.

// Create a texture descriptor.
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
                                                                                             width:textureSize.width
                                                                                            height:textureSize.height
                                                                                         mipmapped:NO];

// Set the texture descriptor's storage mode to `MTLStorageModePrivate`.
textureDescriptor.storageMode = MTLStorageModePrivate;

// Create a private texture.
id <MTLTexture> _privateTexture;
_privateTexture = [_device newTextureWithDescriptor:textureDescriptor];

Finally, encode and commit a copy(from:sourceOffset:sourceBytesPerRow:sourceBytesPerImage:sourceSize:to:destinationSlice:destinationLevel:destinationOrigin:) command. Set the shared buffer as the sourceBuffer parameter. Set the private texture as the destinationTexture parameter.

// Create a command buffer for GPU work.
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

// Encode a blit pass to copy data from the source buffer to the private texture.
id <MTLBlitCommandEncoder> blitCommandEncoder = [commandBuffer blitCommandEncoder];
[blitCommandEncoder copyFromBuffer:_sourceBuffer
                      sourceOffset:0
                 sourceBytesPerRow:pixelSize*textureSize.width
               sourceBytesPerImage:pixelSize*textureSize.width*textureSize.height
                        sourceSize:textureSize
                         toTexture:_privateTexture
                  destinationSlice:0
                  destinationLevel:0
                 destinationOrigin:textureOrigin];
[blitCommandEncoder endEncoding];

// Add a completion handler and commit the command buffer.
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
    // Private texture is populated.
}];
[commandBuffer commit];

Copying Data from a Shared or Managed Texture to a Private Texture

First, create a shared or managed texture and populate its contents.

// Create a common texture descriptor
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
                                                                                             width:textureSize.width
                                                                                            height:textureSize.height
                                                                                         mipmapped:NO];

// Set the texture descriptor's storage mode to `MTLStorageModeShared` or `MTLStorageModeManaged`.
#if TARGET_OS_IOS || TARGET_OS_TVOS
textureDescriptor.storageMode = MTLStorageModeShared;
#endif
#if TARGET_OS_OSX
textureDescriptor.storageMode = MTLStorageModeShared;
#endif

// Create and populate a source texture.
id <MTLTexture> _sourceTexture;
_sourceTexture = [_device newTextureWithDescriptor:textureDescriptor];
[_sourceTexture replaceRegion:MTLRegionMake2D(textureOrigin.x, textureOrigin.y, textureSize.width, textureSize.height)
                  mipmapLevel:0
                    withBytes:textureData
                  bytesPerRow:pixelSize*textureSize.width];

Next, create a private texture with a suitable configuration for your texture data. If appropriate, reuse the texture descriptor that you configured for the shared or managed texture.

// Set the texture descriptor's storage mode to `MTLStorageModePrivate`.
textureDescriptor.storageMode = MTLStorageModePrivate;

// Create a private texture.
id <MTLTexture> _privateTexture;
_privateTexture = [_device newTextureWithDescriptor:textureDescriptor];

Finally, encode and commit a copy(from:sourceSlice:sourceLevel:sourceOrigin:sourceSize:to:destinationSlice:destinationLevel:destinationOrigin:) command. Set the shared or managed texture as the sourceTexture parameter. Set the private texture as the destinationTexture parameter.

// Create a command buffer for GPU work.
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

// Encode a blit pass to copy data from the source texture to the private texture.
id <MTLBlitCommandEncoder> blitCommandEncoder = [commandBuffer blitCommandEncoder];
[blitCommandEncoder copyFromTexture:_sourceTexture
                        sourceSlice:0
                        sourceLevel:0
                       sourceOrigin:textureOrigin
                         sourceSize:textureSize
                          toTexture:_privateTexture
                   destinationSlice:0
                   destinationLevel:0
                  destinationOrigin:textureOrigin];
[blitCommandEncoder endEncoding];

// Add a completion handler and commit the command buffer.
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
    // Private texture is populated.
}];
[commandBuffer commit];

Copying data from a managed texture to a private texture involves two copy operations. For the first operation, Metal synchronizes the managed texture and copies the texture data from CPU-accessible memory to GPU-accessible memory. For the second operation, Metal copies the texture data from the managed texture to the private texture. To copy texture data to a private texture in one copy operation, use the implementation described in Copying Data from a Shared Buffer to a Private Texture.

Copying Data from a Private Texture to a Shared Buffer

Use this implementation to copy texture data from the GPU to a shared buffer, without having to synchronize a managed texture.

First, create a private texture.

// Create a texture descriptor.
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
                                                                                             width:textureSize.width
                                                                                            height:textureSize.height
                                                                                         mipmapped:NO];

// Set the texture descriptor's storage mode to `MTLStorageModePrivate`.
textureDescriptor.storageMode = MTLStorageModePrivate;

// Create a private texture.
id <MTLTexture> _sourceTexture;
_sourceTexture = [_device newTextureWithDescriptor:textureDescriptor];

Next, create a shared buffer that’s large enough to store your texture data.

// Create a shared buffer.
id <MTLBuffer> _sharedBuffer;
_sharedBuffer = [_device newBufferWithLength:pixelSize*textureSize.width*textureSize.height
                                     options:MTLResourceStorageModeShared];

Next, encode a compute, render, or blit pass to populate the contents of your private texture.

// Create a command buffer for GPU work.
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

/* Encode a compute, render, or blit pass to populate the source texture's contents. */
/* ... */

Finally, encode and commit a copy(from:sourceSlice:sourceLevel:sourceOrigin:sourceSize:to:destinationOffset:destinationBytesPerRow:destinationBytesPerImage:) command. Set the private texture as the sourceTexture parameter. Set the shared buffer as the destinationBuffer parameter.

// Encode a blit pass to copy data from the source texture to the shared buffer.
id <MTLBlitCommandEncoder> blitCommandEncoder = [commandBuffer blitCommandEncoder];
[blitCommandEncoder copyFromTexture:_sourceTexture
                        sourceSlice:0
                        sourceLevel:0
                       sourceOrigin:textureOrigin
                         sourceSize:textureSize
                           toBuffer:_sharedBuffer
                  destinationOffset:0
             destinationBytesPerRow:pixelSize*textureSize.width
           destinationBytesPerImage:pixelSize*textureSize.width*textureSize.height];
[blitCommandEncoder endEncoding];

// Add a completion handler and commit the command buffer.
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
    // Shared buffer is populated.
}];
[commandBuffer commit];

See Also

Resources

Setting Resource Storage Modes

Set a storage mode that defines the memory location and access permissions of a resource.

Synchronizing a Managed Resource

Synchronize the contents of a managed resource for the CPU or GPU.

Transferring Data Between Connected GPUs

Use high-speed connections between GPUs to transfer data quickly.

Reducing the Memory Footprint of Metal Apps

Learn best practices for using memory efficiently in iOS and tvOS.

protocol MTLResource

An allocation of memory that is accessible to a GPU.

protocol MTLBlitCommandEncoder

An encoder that encodes memory copying, filtering, and fill commands.

protocol MTLResourceStateCommandEncoder

An encoder that encodes commands that modify resource configurations.

Buffers

Create and manipulate unstructured GPU resources.

Textures

Create and manipulate structured GPU resources.

Indirect Command Buffers

Recoup encoding time by reusing commands, or create a GPU-driven rendering pipeline by generating commands on the GPU.

Heaps

Create a single allocation of memory from which you can suballocate resources.

Synchronization

Manage access to resources in your app to avoid data hazards.