I have a Metal app and I'm trying to export frames to a quicktime movie. I am rendering frames in super hi-res and then scaling them down before writing, in order to antialias the scene.

To scale it, I'm taking the hi-res texture and converting it to a CGImage, then I resize the image and write out the smaller version. I have this extension I found online for converting an MTLTexture to a CGImage:

```
extension MTLTexture {

func bytes() -> UnsafeMutableRawPointer {
    let width = self.width
    let height   = self.height
    let rowBytes = self.width * 4
    let p = malloc(width * height * 4)

    self.getBytes(p!, bytesPerRow: rowBytes, from: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0)

    return p!
}

func toImage() -> CGImage? {
    let p = bytes()

    let pColorSpace = CGColorSpaceCreateDeviceRGB()

    let rawBitmapInfo = CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue // noneSkipFirst
    let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: rawBitmapInfo)

    let size = self.width * self.height * 4
    let rowBytes = self.width * 4

    let releaseMaskImagePixelData: CGDataProviderReleaseDataCallback = { (info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, size: Int) -> () in
        // https://developer.apple.com/reference/coregraphics/cgdataproviderreleasedatacallback
        // N.B. 'CGDataProviderRelease' is unavailable: Core Foundation objects are automatically memory managed
        return
    }
    if let provider = CGDataProvider(dataInfo: nil, data: p, size: size, releaseData: releaseMaskImagePixelData) {

        let cgImageRef = CGImage(width: self.width, height: self.height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: rowBytes, space: pColorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)!
        p.deallocate() //this fixes the memory leak
        return cgImageRef
    }
    p.deallocate() //this fixes the memory leak
    return nil
}

}  // end extension

```

I'm not positive, but it seems like something in this function is resulting in the memory leak -- with every frame it is holding on to the amount of memory in the giant texture / cgimage and not releasing it.

The CGDataProvider initialization takes that 'releaseData' callback argument, but I was under the impression that it was no longer needed.

I also have a resizing extention to CGImage -- this might also cause a leak, I don't know. However, I can comment out the resizing and writing of the frame, and the memory leak still builds up, so it seems to me that the conversion to CGImage is the main problem.

```
extension CGImage {

func resize(_ scale:Float) -> CGImage? {

    let imageWidth = Float(width)
    let imageHeight = Float(height)

    let w = Int(imageWidth * scale)
    let h = Int(imageHeight * scale)

    guard let colorSpace = colorSpace else { return nil }
    guard let context = CGContext(data: nil, width: w, height: h, bitsPerComponent: bitsPerComponent, bytesPerRow: Int(Float(bytesPerRow)*scale), space: colorSpace, bitmapInfo: alphaInfo.rawValue) else { return nil }

    // draw image to context (resizing it)
    context.interpolationQuality = .high
    let r = CGRect(x: 0, y: 0, width: w, height: h)
    context.clear(r)
    context.draw(self, in:r)

    // extract resulting image from context
    return context.makeImage()

}
}

```

Finally, here is the big function that I call every frame when exporting. I'm sorry for the length but it is probably better to provide too much information than too little. So, basically at the start of rendering I allocate a giant MTL texture ('exportTextureBig'), the size of my normal screen multiplied by 'zoom\_subvisions' in each direction. I render the scene in chunks, one for each spot on the grid, and assemble the large frame by using blitCommandEncoder.copy() to copy each small chunk onto the large texture. Once the entire frame is filled in, then I try to make a CGImage from it, scale it down to another CGImage, and write that out.

I'm calling commandBuffer.waitUntilCompleted() every frame while exporting -- hoping to avoid having the renderer hold on to textures that it is still using.

```
func exportFrame2(_ commandBuffer:MTLCommandBuffer, _ texture:MTLTexture)  {  // texture is the offscreen render target for the screen-size chunks

    if zoom_index < zoom_subdivisions*zoom_subdivisions {  // copy screen-size chunk to large texture

        if let blitCommandEncoder = commandBuffer.makeBlitCommandEncoder() {

            let dx = Int(BigRender.globals_L.displaySize.x) * (zoom_index%zoom_subdivisions)
            let dy = Int(BigRender.globals_L.displaySize.y) * (zoom_index/zoom_subdivisions)
            blitCommandEncoder.copy(from:texture,
                                    sourceSlice: 0,
                                    sourceLevel: 0,
                                    sourceOrigin: MTLOrigin(x:0,y:0,z:0),
                                    sourceSize: MTLSize(width:Int(BigRender.globals_L.displaySize.x),height:Int(BigRender.globals_L.displaySize.y), depth:1),
                                    to:BigVideoWriter!.exportTextureBig!,
                                    destinationSlice: 0,
                                    destinationLevel: 0,
                                    destinationOrigin: MTLOrigin(x:dx,y:dy,z:0))

            blitCommandEncoder.synchronize(resource: BigVideoWriter!.exportTextureBig!)
            blitCommandEncoder.endEncoding()
        }

    }

    commandBuffer.commit()
    commandBuffer.waitUntilCompleted() // do this instead

    // is big frame complete?
    if (zoom_index == zoom_subdivisions*zoom_subdivisions-1) {

        // shrink the big texture here

        if let cgImage = self.exportTextureBig!.toImage() {  // memory leak here?

            // this can be commented out and memory leak still happens
            if let smallImage = cgImage.resize(1.0/Float(zoom_subdivisions)) {
                writeFrame(nil, smallImage)
            }

        }

    }

}

```

This all works, except for the huge memory leak. Is there something I can do to make it release the cgImage data each frame? Why is it holding onto it?

Thanks very much for any suggestions!