【问题标题】:Metal seems to interpret bgra8Unorm as an rgba colorMetal 似乎将 bgra8Unorm 解释为 rgba 颜色
【发布时间】:2021-04-23 05:42:45
【问题描述】:

在 Apple Metal 应用程序中,我正在尝试编辑纹理内容。纹理使用 bgra8Unorm 像素格式,创建方式如下:

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)

我将像素颜色设置如下:

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))
}

主像素格式也设置为bgra8Unorm继承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 float4 basic_fragment(
                              VertexOut inVert [[ stage_in ]],
                              texture2d<float> tex2D [[ texture(0) ]],
                              sampler sampler2D [[ sampler(0) ]]
                              ) {
    return tex2D.sample(sampler2D, inVert.tex);
}

但是,当我调用所有这些来将像素的颜色设置为NSColor.orange 时,它似乎是蓝色的。

运行调试器并捕获 GPU 帧,我可以看到缓冲区中的值是 8388607,看起来像 maxed AlphaRed 和几乎 maxed Green。但不知何故,Metal 似乎将这种颜色解释为rgba 格式,尽管我将所有内容都设置为bgra

我做错了吗?

【问题讨论】:

    标签: swift metal


    【解决方案1】:

    我做错了吗?

    bgra表示在内存中依次出现bgra这4个分量。 所以,在 little endian 中,UInt32b 应该是 LS 字节,a 应该是 MS 字节。

    请试试这个:

        ptr[index] = (UInt32(a * 255) << 24) | (UInt32(r * 255) << 16) | (UInt32(g * 255) << 8) | UInt32(b * 255)
    

    【讨论】:

    • 对。这似乎有效。我在文档中找到一句话说颜色存储在 little-endian 中。然而,在我的代码中,我使用的是 UInt32,不知何故,我认为几乎所有现代架构也是 little-endian。因此,我希望内存写入能够正确完成所有操作。这最终是否意味着 Apple M1 芯片使用大端序,还是我遗漏了什么?
    • @saferif,M1芯片和Intel一样是little-endian,在little-endian中,最低有效字节在前。
    • 我知道你已经回答了我最初的问题,这一切可能超出了范围,但如果你能帮助我理解这一点,我将不胜感激。如果 M1 芯片和 GPU 都使用 little-endian 字节顺序,那么字节顺序如何变得混乱?内存写入是否应该正确完成所有操作。在 little-endian 中,最低有效字节在前。在我的原始代码中,最不重要的是a,不应该像预期的那样先写吗?
    • @saferif,“最不重要的是a”——对,“不应该按预期先写吗?” - 这应该。但是您指定了bgra,因此Metal 期望第一个字节为b
    • 哦,我知道了。我认为bgra 意味着b 是MSB 而a 是LSB,当用小端写时,它会自动变成正确的东西。非常感谢您的澄清和帮助。
    猜你喜欢
    • 2016-05-16
    • 2011-01-04
    • 2012-07-07
    • 2018-06-09
    • 2019-03-26
    • 1970-01-01
    • 2015-10-14
    • 1970-01-01
    • 2015-05-19
    相关资源
    最近更新 更多