Search code examples
iosmetalcvpixelbuffer

getBytes from MtlTexture to create CVPixelBufferRef


Trying to create CVPixelBufferRef from MTLTexture on each call render of SCNRender object:

  CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
        let bytesPerRow = 4 * Int(textureSizeX)
        let region = MTLRegionMake2D(0, 0, Int(textureSizeX), Int(textureSizeY))

    var tmpBuffer = CVPixelBufferGetBaseAddress(pixelBuffer!);

    offscreenTexture.getBytes(tmpBuffer!, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)

    CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))

And then convert to UIImage to display on screen

    let ciImage = CIImage.init(cvPixelBuffer: pixelBuffer!)
    let temporaryContext = CIContext(options: nil)
    let tempImage = temporaryContext.createCGImage(ciImage, from: CGRect(x: 0, y: 0, width: textureSizeX, height: textureSizeY))
    let uiImage = UIImage.init(cgImage: tempImage!)

But image is not displayed


Solution

  • You should think of it the other way around, create a metal texture object that uses a CoreVideo buffer as the backing buffer and then render into the texture in the normal way.

    - (id<MTLTexture>) makeBGRACoreVideoTexture:(CGSize)size
                            cvPixelBufferRefPtr:(CVPixelBufferRef*)cvPixelBufferRefPtr
    {
      int width = (int) size.width;
      int height = (int) size.height;
    
      // CoreVideo pixel buffer backing the indexes texture
    
      NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                               [NSNumber numberWithBool:YES], kCVPixelBufferMetalCompatibilityKey,
                               [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                               [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                               nil];
    
      CVPixelBufferRef pxbuffer = NULL;
    
      CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
                                            width,
                                            height,
                                            kCVPixelFormatType_32BGRA,
                                            (__bridge CFDictionaryRef) options,
                                            &pxbuffer);
    
      NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
    
      *cvPixelBufferRefPtr = pxbuffer;
    
      CVMetalTextureRef cvTexture = NULL;
    
      CVReturn ret = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                               _textureCache,
                                                               pxbuffer,
                                                               nil,
                                                               MTLPixelFormatBGRA8Unorm,
                                                               CVPixelBufferGetWidth(pxbuffer),
                                                               CVPixelBufferGetHeight(pxbuffer),
                                                               0,
                                                               &cvTexture);
    
      NSParameterAssert(ret == kCVReturnSuccess && cvTexture != NULL);
    
      id<MTLTexture> metalTexture = CVMetalTextureGetTexture(cvTexture);
    
      CFRelease(cvTexture);
    
      return metalTexture;
    }