For memory management purpose I need to know how much memory is needed for Metal textures. I know I need to page align the buffer to estimate the size, but for some reason, my estimation is incorrect.
Consider this pseudo-code:
import Darwin
import Metal
import MetalKit
import UIKit
func estimate(width: Int, height: Int) -> Int {
let pageSize = Int(getpagesize())
let unalignedSize = width * height * 4 // let's assume 32bits ARGB texture
return ((unalignedSize + pageSize - 1) / pageSize) * pageSize
}
func actually(width: Int, height: Int) -> Int {
let l = MTKTextureLoader(device: MTLCreateSystemDefaultDevice()!)
let options : [MTKTextureLoader.Option : NSObject] = [
MTKTextureLoader.Option.allocateMipmaps : NSNumber(booleanLiteral: false),
MTKTextureLoader.Option.generateMipmaps : NSNumber(booleanLiteral: false),
MTKTextureLoader.Option.textureUsage : NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
MTKTextureLoader.Option.SRGB : NSNumber(booleanLiteral: false)
]
let t = try! l.newTexture(cgImage: generateMonochromeImage(color: .blue, size: CGSize(width: width, height: height)), options: options)
return t.allocatedSize
}
func generateMonochromeImage(color: UIColor, size: CGSize) -> CGImage {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 1.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image.cgImage!
}
estimate(width: 414, height: 512) //-> 851968
actually(width: 414, height: 512) //-> 917504
estimate(width: 363, height: 512) //-> 753664
actually(width: 363, height: 512) //-> 786432
As you can see estimations are off...
Is there a way to predict/compute how much a MTLTexture will allocate?
At least one issue is the row byte alignment (a.k.a. bytesPerRow
). For the hardware, accessing rows that are aligned to certain powers of two is more efficient. So, that's how data is stored internally.
From your numbers, it seems the texture is using 256 bytes per row. So a row that's 414 pixels wide with 4 bytes per pixel would require 1656 bytes. But that's not a multiple of 256. So, the rows will be padded out to the next multiple of 256, which is 1792. That times 512 rows is 917504.
But, as Idoogy mentions in a comment, that's not predictable across all hardware.