Creating a Metal texture with float data

Hi,


I'm trying to transition my macOS application to use Metal. It uses CIFilter's extensively. I'm trying to create a Metal texture for use in a MTKView subclass. The source data is an array of float values, not an RGB image. I add color later with a CIFilter colormap. The existing code (that works) creates a CIImage like this:


NSData *convertedData = <float data>
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)convertedData);
CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrder32Host | kCGBitmapFloatComponents;
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);

CGImageRef cgImage = CGImageCreate(width, // size_t width
                                   height, // size_t height
                                   32, // size_t bitsPerComponent (float=32)
                                   32, // size_t bitsPerPixel == bitsPerComponent for float
                                   bytesPerRow, // size_t bytesPerRow -> width in pixels * sizeof(float)
                                   colorSpace, // CGColorSpaceRef
                                   bitmapInfo, // CGBitmapInfo
                                   dataProvider, // CGDataProviderRef
                                   NULL, // const CGFloat decode[] - NULL = do not want to allow
                                         // remapping of the image’s color values
                                   NO, // shouldInterpolate?
                                   kCGRenderingIntentDefault); // CGColorRenderingIntent

CIImage *ciImage = [CIImage imageWithCGImage:cgImage options:@{kCIImageColorSpace: [NSNull null]}];

I tried to convert this into a Metal texture:


MTKTextureLoader *textureLoader = [[MTKTextureLoader alloc] initWithDevice:self.device];

NSError *error = nil;
NSNumber *textureUsageOptions = @(MTLTextureUsageUnknown); // TODO: revisit this when things work
texture = [textureLoader newTextureWithCGImage:ciImage.CGImage // CGImageRef
                                       options:@{
                                          MTKTextureLoaderOptionTextureUsage:textureUsageOptions,
                                          MTKTextureLoaderOptionSRGB:@(0) // image data is linear
                                        }
                                         error:&error];


I get this error:


Error Domain=MTKTextureLoaderErrorDomain Code=0 "Image decoding failed"
UserInfo={NSLocalizedDescription=Image decoding failed, MTKTextureLoaderErrorKey=Image decoding failed}


Is this the right approach? I don't tend to see many examples using float components to create images.


Thanks!

Answered by demitri in 423001022

In case anyone needs the answer, the solution is to create a texture descriptor directly from the data:


MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR32Float
  width:width
  height:height
  mipmapped:NO];

Create a new texture using that descriptor ([device newTextureWithDescriptor]), then use the "replaceRegion:mipmapLevel..." method to copy the data directly into the texture buffer.

Accepted Answer

In case anyone needs the answer, the solution is to create a texture descriptor directly from the data:


MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR32Float
  width:width
  height:height
  mipmapped:NO];

Create a new texture using that descriptor ([device newTextureWithDescriptor]), then use the "replaceRegion:mipmapLevel..." method to copy the data directly into the texture buffer.

Creating a Metal texture with float data
 
 
Q