How to specify device for Metal Compute in CIImageProcessorKernel subclass?

394 Views Asked by At

I've got a custom CIFilter, implemented using a subclass of CIImageProcessorKernel. The kernel itself is very simple:

@implementation ErosionFilterKernel

+ (BOOL)processWithInputs:(NSArray<id<CIImageProcessorInput>> *)inputs
                arguments:(NSDictionary<NSString *,id> *)arguments
                   output:(id<CIImageProcessorOutput>)output
                    error:(NSError *__autoreleasing *)error
{
    error = error ?: &(NSError * __autoreleasing){ nil };

    id<MTLCommandBuffer> commandBuffer = output.metalCommandBuffer;
    id<MTLTexture> sourceTexture = [inputs.firstObject metalTexture];
    id<MTLTexture> destinationTexture = output.metalTexture;

    NSInteger distance = [arguments[@"erosionDistance"] integerValue] ?: 1;

    MPSImageAreaMin *erodeFilter = [[MPSImageAreaMin alloc] initWithDevice:commandBuffer.device
                                                               kernelWidth:distance
                                                              kernelHeight:distance];
    [erodeFilter encodeToCommandBuffer:commandBuffer sourceTexture:sourceTexture destinationTexture:destinationTexture];
    return YES;
}

@end

This works fine, in that it produces the expected result. The problem I'm having is that it uses the integrated GPU on a MacBook Pro with two GPUs, and I'd like it to use the discrete GPU. If I pass the result of MTLCreateSystemDefaultDevice() (the discrete GPU) into -[MPSImageAreaMin initWithDevice:...], I get an assertion failure:

-[MTLDebugComputeCommandEncoder setComputePipelineState:] failed assertion computePipelineState is associated with a different device

This is presumably because the instance of MTLComputeCommandEncoder used internally by the machinery responsible for running -encodeToCommandBuffer:sourceTexture:destinationTexture: has already been set up to use the integrated GPU. I think this comes from the commandBuffer pulled from the CIImageProcessorOutput object.

My question: Is it possible to specify the GPU to be used by -encodeToCommandBuffer:sourceTexture:destinationTexture:? Presumably that involves customizing the output/metal command buffer, but I'm not sure about that.

1

There are 1 best solutions below

1
On BEST ANSWER

Which GPU is used should be determined by the CIContext that performs the image processing. You should be able to specify the device by using the [CIContext contextWithMTLDevice:] initializer.

By the way, since macOS 10.15 there's also a built-in CIMorphologyRectangleMinimum filter that does the same. And there's also a circular version that's available since 10.13.