Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I read those MTLTexture pixels?

Tags:

swift

metal

I am trying to get the pixels of a MTLTexture this way:

let pixelCount = compareTexture.width * compareTexture.height

let region = MTLRegionMake2D(0, 0, compareTexture.width, compareTexture.height)  

var textureComponentsArray = Array<float4>(repeating: float4(0), count: pixelCount)

textureComponentsArray.withUnsafeMutableBytes {
   compareTexture.getBytes($0.baseAddress!, bytesPerRow: (MemoryLayout<float4>.size * compareTexture.width), from: region, mipmapLevel: 0)
}

print(textureComponentsArray.first!)

Unfortunately most elements are either NaN or equal to the values which I initialised my textureComponentsArray with. For instance this code prints :

float4(nan, nan, nan, nan)

I am on macOS and my MTLTexture has those properties:

let textureDescriptor = MTLTextureDescriptor()
textureDescriptor.width = imageTexture.width
textureDescriptor.height = imageTexture.height
textureDescriptor.pixelFormat = imageTexture.pixelFormat
textureDescriptor.resourceOptions = .storageModeManaged
textureDescriptor.storageMode = .managed
textureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite]

I do use the managed storage mode so the data should be available to CPU, I don't understand why it doesn't work.

Thank you.

EDIT :
I am trying to use :

blitCommandEncoder.synchronize(resource: compareTexture)

And I do wait the command buffer to complete but there is still the issue.

like image 588
Pop Flamingo Avatar asked Jan 10 '17 20:01

Pop Flamingo


1 Answers

I needed an answer for this question for iOS. I'm leaving here the extension I wrote for my needs.

import Metal

extension MTLTexture {
  func getPixels<T> (_ region: MTLRegion? = nil, mipmapLevel: Int = 0) -> UnsafeMutablePointer<T> {
    let fromRegion  = region ?? MTLRegionMake2D(0, 0, self.width, self.height)
    let width       = fromRegion.size.width
    let height      = fromRegion.size.height
    let bytesPerRow = MemoryLayout<T>.stride * width
    let data        = UnsafeMutablePointer<T>.allocate(capacity: bytesPerRow * height)

    self.getBytes(data, bytesPerRow: bytesPerRow, from: fromRegion, mipmapLevel: mipmapLevel)
    return data
  }
}

You can simply call the function. But remember that you are responsible for deallocating the memory used for the data object.

You can read the pixels if they are in different formats too. For a MTLPixelFormat.rgba32Float texture I'm retrieving the data as:

let pixels: UnsafeMutablePointer<Float32> = texture.getPixels()
defer {
  pixels.deallocate()
}

let capacity = texture.width * texture.height * MemoryLayout<Float32>.stride

for i in stride(from: 0, to: capacity, by: 4) {
  let l     = pixels[i + 0]
  let a     = pixels[i + 1]
  let b     = pixels[i + 2]
  let alpha = pixels[i + 3]
}
like image 158
emrahgunduz Avatar answered Oct 03 '22 11:10

emrahgunduz