1

Hi I am trying to convert below objective c code into swift but struggling to convert unions which are supported into C but not directly in swift.

I am not sure how I can convert below union type and pass it to MTLTexture getbytes?


union {
    float f[2];
    unsigned char bytes[8];
} u;

Also last part where I want to print these float values with log statement.

It would be great if I get working swift conversion for below code snippet.


id<MTLDevice> device = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> queue = [device newCommandQueue];
id<MTLCommandBuffer> commandBuffer = [queue commandBuffer];

MTKTextureLoader *textureLoader = [[MTKTextureLoader alloc] initWithDevice:device];
id<MTLTexture> sourceTexture = [textureLoader newTextureWithCGImage:image.CGImage options:nil error:nil];


CGColorSpaceRef srcColorSpace = CGColorSpaceCreateDeviceRGB();
CGColorSpaceRef dstColorSpace = CGColorSpaceCreateDeviceGray();
CGColorConversionInfoRef conversionInfo = CGColorConversionInfoCreate(srcColorSpace, dstColorSpace);
MPSImageConversion *conversion = [[MPSImageConversion alloc] initWithDevice:device
                                                                   srcAlpha:MPSAlphaTypeAlphaIsOne
                                                                  destAlpha:MPSAlphaTypeAlphaIsOne
                                                            backgroundColor:nil
                                                             conversionInfo:conversionInfo];
MTLTextureDescriptor *grayTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR16Unorm
                                                                                                 width:sourceTexture.width
                                                                                                height:sourceTexture.height
                                                                                             mipmapped:false];
grayTextureDescriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
id<MTLTexture> grayTexture = [device newTextureWithDescriptor:grayTextureDescriptor];
[conversion encodeToCommandBuffer:commandBuffer sourceTexture:sourceTexture destinationTexture:grayTexture];


MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:grayTexture.pixelFormat
                                                                                             width:sourceTexture.width
                                                                                            height:sourceTexture.height
                                                                                         mipmapped:false];
textureDescriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
id<MTLTexture> texture = [device newTextureWithDescriptor:textureDescriptor];

MPSImageLaplacian *imageKernel = [[MPSImageLaplacian alloc] initWithDevice:device];
[imageKernel encodeToCommandBuffer:commandBuffer sourceTexture:grayTexture destinationTexture:texture];


MPSImageStatisticsMeanAndVariance *meanAndVariance = [[MPSImageStatisticsMeanAndVariance alloc] initWithDevice:device];
MTLTextureDescriptor *varianceTextureDescriptor = [MTLTextureDescriptor
                                                   texture2DDescriptorWithPixelFormat:MTLPixelFormatR32Float
                                                   width:2
                                                   height:1
                                                   mipmapped:NO];
varianceTextureDescriptor.usage = MTLTextureUsageShaderWrite;
id<MTLTexture> varianceTexture = [device newTextureWithDescriptor:varianceTextureDescriptor];
[meanAndVariance encodeToCommandBuffer:commandBuffer sourceTexture:texture destinationTexture:varianceTexture];


[commandBuffer commit];
[commandBuffer waitUntilCompleted];

union {
    float f[2];
    unsigned char bytes[8];
} u;

MTLRegion region = MTLRegionMake2D(0, 0, 2, 1);
[varianceTexture getBytes:u.bytes bytesPerRow:2 * 4 fromRegion:region mipmapLevel: 0];

NSLog(@"mean: %f", u.f[0] * 255);
NSLog(@"variance: %f", u.f[1] * 255 * 255);

It will be great if I get swift representation for this?

Kapil
  • 133
  • 2
  • 11
  • When I convert union into struct and pass it to getBytes function I do get error - cannot convert value of struct into UnsafeMutableRawPointer – Kapil Mar 10 '21 at 03:55

2 Answers2

1

You can use a Struct instead, like this. And add an extension to get description for logging.


struct u {

    var bytes: [UInt8] = [0,0,0,0, 0,0,0,0]

    var f: [Float32] {
        set {
            var f = newValue
            memcpy(&bytes, &f, 8)
        }
        get {
            var f: [Float32] = [0,0]
            var b = bytes
            memcpy(&f, &b, 8)
            return Array(f)
        }
    }
}

extension u: CustomStringConvertible {
    var description: String {
        let bytesString = (bytes.map{ "\($0)"}).joined(separator: " ")
        return "floats : \(f[0]) \(f[1]) - bytes  : \(bytesString)"
    }
}


var test = u()
print(test)
test.f = [3.14, 1.618]
print(test)
test.bytes = [195, 245, 72, 64, 160, 26, 207, 63]
print(test)

Log:

floats : 0.0 0.0 - bytes  : 0 0 0 0 0 0 0 0
floats : 3.14 1.618 - bytes  : 195 245 72 64 160 26 207 63
floats : 3.14 1.618 - bytes  : 195 245 72 64 160 26 207 63
Moose
  • 2,607
  • 24
  • 23
0

You don't need the whole union for getBytes to work, only u.bytes is used there, which can be converted as

var bytes = [UInt8](repeating: 0, count: 8)

that's the array of length 8 (with arbitrary initial value 0 in each element), and you pass it to getBytes as an UnsafeMutableRawPointer:

varianceTexture.getBytes(&bytes, ...)

As for union, there are many ways to represent it. For example:

var u = ([Float](repeating: 0.0, count: 2), [UInt8](repeating: 0, count: 8))

And in that case you pass it as

varianceTexture.getBytes(&u.1, ...)

Or you could make it a class or struct in a similar way.

timbre timbre
  • 12,648
  • 10
  • 46
  • 77
  • that hack is not available for you unfortunately, since there's no way in swift to express the same intent (saving 2 pieces of info in the same memory space). So you would have to convert `bytes` to `f` like this: https://stackoverflow.com/questions/41161034/how-to-convert-bytes-to-a-float-value-in-swift – timbre timbre Mar 10 '21 at 16:06
  • sorry I am not following can you provide example for below. I am using now union like this ```var u = ([Float](repeating: 0.0, count: 2), [UInt8](repeating: 0, count: 8))``` and now I would like print like I shown in objective example. ```NSLog(@"mean: %f", u.f[0] * 255); NSLog(@"variance: %f", u.f[1] * 255 * 255);``` – Kapil Mar 10 '21 at 20:45
  • Can you please help here? – Kapil Mar 10 '21 at 20:46