0

I am using MPSImageLanczosScale to scale image texture (initiated from CVPixelBufferRef) using Metal framework. The issue is MPSImageLanczosScale is only available from 10.13. But my application supports from 10.11. I can not stop supporting the lower OS versions since there are still many users using those versions. Is there any alternative way of scaling the image using Metal(or using any other way)?

Note: I am using Metal as I need GPU based image scaling to avoid any cpu consumption. So I'm looking for a GPU based image scaling solution.

Including the current implementation for your reference.

-(CVImageBufferRef)rescaleGPU:(CVImageBufferRef)sourceImageBuffer {
    CVReturn error;

    CGFloat backingScaleFactor = [[NSScreen mainScreen] backingScaleFactor];

    CVMetalTextureRef textureRef;
    error = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, sourceImageBuffer, NULL, MTLPixelFormatBGRA8Unorm, g_screenWidth * backingScaleFactor, g_screenHeight * backingScaleFactor, 0, &textureRef);

    id <MTLTexture> _metalTexture = CVMetalTextureGetTexture(textureRef);

    id<MTLTexture> _destinationTexture = [_device newTextureWithDescriptor:self.textureDescriptor];

    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    [self.scaleFilter encodeToCommandBuffer:commandBuffer sourceTexture:_metalTexture destinationTexture:_destinationTexture];

    id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
    [blitEncoder synchronizeTexture:_destinationTexture slice:0 level:0];
    [blitEncoder endEncoding];

    [commandBuffer commit];
    [commandBuffer waitUntilCompleted];
    

    void* destData = malloc(scaleHeight * scaleWidth * 4);

    [_destinationTexture getBytes:destData bytesPerRow:scaleWidth * 4 fromRegion:self.destinationRegion mipmapLevel:0];
    
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, scaleWidth, scaleHeight, kCVPixelFormatType_32BGRA, destData, scaleWidth * 4, NULL, NULL, NULL, &finalBuffer);

    CVBufferRelease(textureRef);
    [_destinationTexture release];
    free(destData);
    
    return finalBuffer;
}
prabhu
  • 1,158
  • 1
  • 12
  • 27

1 Answers1

0

You can fall back to MPSImageBilinearScale for users that are on versions that don't have the MPSImageLanczosScale. It will be lower quality, but you won't have to roll your own resize and also won't have to abandon existing users that didn't update their OS.

Make scaleFilter member of your class be of type MPSImageScale*

if (@available(macOS 10.13, *))
{
    self.scaleFilter = [[MPSImageLanczosScale alloc] initWithDevice:device];
}
else 
{
    self.scaleFilter = [[MPSImageBilinearScale alloc] initWithDevice:device];
}

They have the same base interface MPSUnaryImageKernel so you won't have to change other parts of the code, at least from what I can tell.

JustSomeGuy
  • 3,677
  • 1
  • 23
  • 31