0 Replies
      Latest reply on Nov 4, 2019 12:53 AM by DrAma
      DrAma Level 1 Level 1 (0 points)

        In the last few weeks in my spare time I've tried to convert a `CIFilter` with a custom color kernel from a string to a metal written kernel.

        The purpose of this filter is to pixellate an image and then apply a custom palette of colors. Pixellation is made by CI Pixellate and then I apply this shader.

        float4 eight_bit_buffer(sampler image, constant simd_float3 palette[], float paletteSize, destination dest) {
                float4 color = image.sample(image.transform(dest.coord()));
                float dist = distance(color.rgb, palette[0]);
                float3 returnColor = palette[0];
                for (int i = 1; i < floor(paletteSize); ++i) {
                    float tempDist = distance(color.rgb, palette[i]);
                    if (tempDist < dist) {
                        dist = tempDist;
                        returnColor = palette[i];
                return float4(returnColor, 1);

        The first argument is the image the second is an array of float3 that makes the palette of color.

        Since iOS13 there was written this in the change note:

        Metal CIKernel instances support arguments with arbitrarily structured data but I couldn't find any reference in the the documentation or videos.

        I've tried to do my myself basically trying to create an Data object and pass it to the shader function. At a first try I simply created a Data object and appended SIM3<Double> to it, but it wasn't working so I tought it could be an issue with memory allignment and I came up with this, but it won't work either.

        func toSIMD3() -> SIMD3<Float> {
                 return [Float(r) / 255.0, Float(g) / 255.0, Float(b) / 255.0]
        static func SIMD3Palette(_ palette: [RGB]) -> [SIMD3<Float>] {
                return palette.map{ $0.toSIMD3() }
        static func toSIMD3Buffer(from palette: [RGB]) -> Data {
                var simd3Palette = SIMD3Palette(palette)
                let size = MemoryLayout<SIMD3<Float>>.size
                let count = palette.count
                let palettePointer = UnsafeMutableRawPointer.allocate(
                    byteCount: count * MemoryLayout<SIMD3<Float>>.stride,
                    alignment: MemoryLayout<SIMD3<Float>>.alignment)
                let simd3Pointer = simd3Palette.withUnsafeMutableBufferPointer { (buffer) -> UnsafeMutablePointer<SIMD3<Float>> in
                    let p = palettePointer.initializeMemory(as: SIMD3<Float>.self,
                    from: buffer.baseAddress!,
                    count: buffer.count)
                    return p
                let data = Data(bytesNoCopy: simd3Pointer, count: count * MemoryLayout<simd3>.stride, deallocator: .free)
                return data

        Have anyone discovered how to pass arbitrarily structured data to a CIKernel?