1

I have a simple Metal CIWarpKernel:

    float2 MyWarp(destination dest, float offset)
    {
        return float2(dest.coord().x + offset, dest.coord().y);
    }

and:

class MyWarpFilter : CIFilter
{
    var inputImage: CIImage?
    var inputOffset: Float = 100.0

    static var kernel: CIWarpKernel =
    { () -> CIWarpKernel in
        let url = Bundle.main.url(forResource: "MyWarp", withExtension: "ci.metallib")!
        let data = try! Data(contentsOf: url)
        return try! CIWarpKernel(functionName: "MyWarp", fromMetalLibraryData: data) <== ERROR
    }()

    override var outputImage : CIImage?
    {
        get
        {
            guard let input = inputImage else { return nil }

            let roiCallback: CIKernelROICallback =
            { _, rect -> CGRect in
                return CGRect(x: rect.minX, y: rect.minY, width: input.extent.width, height: input.extent.height)
            }

            let arguments: [Any] = [inputOffset]

            return MyWarpFilter.kernel.apply(extent: input.extent,
                                             roiCallback: roiCallback,
                                             image: input, arguments: arguments)
        }
    }
}

When I run this, I get the following run-time error (at line indicated above with <== ERROR):

Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Foundation._GenericObjCError.nilError

If I remove the second MyWarp() parameter, run the filter with arguments: [] and have a hard-coded offset, there's no error (and the filter translates the image by offset).

What am I doing wrong with passing arguments to the CIWarpKernel?

meaning-matters
  • 21,929
  • 10
  • 82
  • 142
  • 1
    By the way, you can achieve all that much easier: `let offsetImage = input.transformed(by: CGAffineTransform(translationX: inputOffset, y: 0)` – Frank Rupprecht Mar 20 '21 at 19:15
  • @FrankSchlegel That's true. This was the simplest version to show my issue. My real Metal kernel uses trigonometric functions and other stuff. (BTW, to get the same result, should be `translationX: -inputOffset`.) – meaning-matters Mar 21 '21 at 09:56

1 Answers1

3

Running your code with iOS Simulator 14.4/Xcode 12.4, I get the following error: (Not sure I made something wrong or this is just a problem of runtime versions.)

Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=CIKernel Code=4 "(null)" UserInfo={CINonLocalizedDescriptionKey=If specified, destination must be the last parameter of a CIKernel function.}

So, at least, you may need to move the parameter destination dest to the last:

    float2 MyWarp(float offset, destination dest)
    {
        return float2(dest.coord().x + offset, dest.coord().y);
    }

Metal Shading Language for Core Image Kernels

destination A kernel parameter type that allows access to the position of the pixel currently being computed. This parameter, which is required for CIWarpKernel and optional for CIColorKernel and CIKernel, must be the last parameter to a kernel function.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • 1
    I didn't get that very informative error. But this is indeed what resolves my issue. Many thanks. (I'd been looking in Apple documentation and other sources, but there's very little on CI Metal shaders.) – meaning-matters Mar 20 '21 at 16:05