Chen OT
Chen OT

Reputation: 3614

How to set texture storage mode to `private` to texture created from `CVMetalTextureCacheCreateTextureFromImage`?

Xcode's GPU frame capture highlight multiple expressions as purple and say I should set the texture storage mode to private because only GPU access it. I am trying to fix the purple suggestion.

Memory Usage'Texture:0x10499ae00 "CoreVideo 0x6000017f2bc0"' has storage mode 'Managed' but is accessed exclusively by a GPU

When using device.makeBuffer(bytes:length:options:) to create MTLTexture, I can set storageMode to private in the argument options.

But when create MTLTexture from CVPixelBuffer through CVMetalTextureCacheCreateTextureFromImage(), I don't know how to configure the storage mode for the created texture.

Ways I tried:

var textureAttrs: [String: Any] = [:]
if #available(macOS 10.15, *) {
    textureAttrs[kCVMetalTextureStorageMode as String] = MTLStorageMode.private
}
CVMetalTextureCacheCreateTextureFromImage(,,,textureAttrs as CFDictionary,..., &texture)

if let texture = texture,
    let metalTexture = CVMetalTextureGetTexture(texture) {
        print(metalTexture.storageMode.rawValue)
    }
}

My OS is already 10.15.4, but the created MTLTexture still has storageMode as managed/rawValue: 1

The result is the same.

Problems:

References:

Upvotes: 4

Views: 1152

Answers (2)

JoeBayLD
JoeBayLD

Reputation: 999

To set the texture attributes - you muse use the rawValue of the MTLStorageMode. For example:

    var textureAttrs: [String: Any] = [:]
    
    if #available(macOS 10.15, *) {
        result[kCVMetalTextureStorageMode as String] = MTLStorageMode.managed.rawValue
    }

    var textureCache: CVMetalTextureCache!
    let textureCache = CVMetalTextureCacheCreate(
            nil, nil, device, textureAttrs as CFDictionary, &self.textureCache)

Once the cache is created with those texture attributes, you can pass nil as the texture attributes in CVMetalTextureCacheCreateTextureFromImage when creating each texture as it will use whatever storage mode the cache was created with. For example:

      var cvTexture: CVMetalTexture?
      CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, .bgra8Unorm, width, height, 0, &cvTexture)

Something to note - I was getting metal warnings in Xcode that my textures should be made private instead of managed, but when setting the cache to a private storage mode, the following error occurred:

failed assertion 'Texture Descriptor Validation IOSurface textures must use MTLStorageModeManaged'

This is because these textures are IOSurface-backed. So for now I'm keeping it managed.

Upvotes: 0

rokstar
rokstar

Reputation: 454

I have experience with Metal and had the same kind of issue. There is no way to change texture storageMode one the fly. You have to create another MTLTexture with desired storageMode and use MTLBlitCommandEncoder to copy data to it.

Here is the piece of code from my project:

    MTLTextureDescriptor* descriptor = [[MTLTextureDescriptor alloc] init];
    descriptor.storageMode = MTLStorageModePrivate;
    descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;
    descriptor.width = width;
    descriptor.height = height;

    id<MTLTexture> texture = [__metal_device newTextureWithDescriptor: descriptor];

    if ((data != NULL) && (size > 0)) {
        id<MTLCommandQueue> command_queue = [__metal_device newCommandQueue];
        id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
        id<MTLBlitCommandEncoder> command_encoder = [command_buffer blitCommandEncoder];
        id<MTLBuffer> buffer = [__metal_device newBufferWithBytes: data
                                                           length: size
                                                          options: MTLResourceStorageModeShared];
        [command_encoder copyFromBuffer: buffer
                           sourceOffset: 0
                      sourceBytesPerRow: (width * 4)
                    sourceBytesPerImage: (width * height * 4)
                             sourceSize: (MTLSize){ width, height, 1 }
                              toTexture: texture
                       destinationSlice: 0
                       destinationLevel: 0
                      destinationOrigin: (MTLOrigin){ 0, 0, 0 }];

        [command_encoder endEncoding];
        [command_buffer commit];
        [command_buffer waitUntilCompleted];
    }

Upvotes: 0

Related Questions