In an Apple Metal app I am trying to edit texture contents. Texture uses bgra8Unorm pixel format and is created like this:
fileprivate var texture: MTLTexture!
private var buffer: MTLBuffer!
private var width: Int!
private var height: Int!
...
buffer = device.makeBuffer(length: width * height * 4, options: .storageModeManaged)
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, width: width, height: height, mipmapped: false)
texture = buffer.makeTexture(descriptor: textureDescriptor, offset: 0, bytesPerRow: width * 4)
I am setting pixel color as following:
func setPixel(x: Int, y: Int, color: NSColor) {
let ptr = buffer.contents().bindMemory(to: UInt32.self, capacity: buffer.length / 4)
var b: CGFloat = 0
var g: CGFloat = 0
var r: CGFloat = 0
var a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
let index = y * width + x
ptr[index] = (UInt32(b * 255) << 24) | (UInt32(g * 255) << 16) | (UInt32(r * 255) << 8) | UInt32(a * 255)
buffer.didModifyRange(index..<(index + 1))
}
Main pixel format is also set to bgra8Unorm
inheriting MTKView
:
init() {
super.init(frame: .zero, device: MTLCreateSystemDefaultDevice())
colorPixelFormat = .bgra8Unorm
clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
}
Fragment shader doesn't do anything interesting at all:
fragment float4 basic_fragment(
VertexOut inVert [[ stage_in ]],
texture2d<float> tex2D [[ texture(0) ]],
sampler sampler2D [[ sampler(0) ]]
) {
return tex2D.sample(sampler2D, inVert.tex);
}
However, when I am calling all this to set the color of the pixel to be NSColor.orange
is appears to be blue.
Running the debugger and capturing the GPU frame, I can see that the value in the buffer is 8388607
which looks like maxed Alpha
, Red
, and almost maxed Green
. But somehow Metal seems to interpret this color as being in rgba
format, despite me setting everything to bgra
Am I doing something wrong?
Am I doing something wrong?
bgra
means that 4 components b
, g
, r
and a
appears in this order in memory.
So, in little endian UInt32
, b
should be the LS byte and a
the MS byte.
Please try this:
ptr[index] = (UInt32(a * 255) << 24) | (UInt32(r * 255) << 16) | (UInt32(g * 255) << 8) | UInt32(b * 255)