检测 MTLTexture 是否为空白

J.D*_*Doe 0 ios swift metal

我试图通过计算每个像素的所有 RGB 和 A 分量的总和来确定 MTLTexture(bgra8Unorm 格式)是否为空白。

此函数旨在通过在将纹理复制到指针后在内存中添加相邻的浮点数来实现此目的。但是我已经确定,无论给定的 MTLTexture,这个函数最终都会返回 false。

这个功能有什么问题?

func anythingHere(_ texture: MTLTexture) -> Bool {
        let width = texture.width
        let height = texture.height
        let bytesPerRow = width * 4

        let data = UnsafeMutableRawPointer.allocate(bytes: bytesPerRow * height, alignedTo: 4)
        defer {
            data.deallocate(bytes: bytesPerRow * height, alignedTo: 4)
        }

        let region = MTLRegionMake2D(0, 0, width, height)
        texture.getBytes(data, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
        var bind = data.assumingMemoryBound(to: UInt8.self)

        var sum:UInt8 = 0;
        for i in 0..<width*height {
            sum += bind.pointee
            bind.advanced(by: 1)
        }
        return sum != 0
}
Run Code Online (Sandbox Code Playgroud)

war*_*enm 5

Matthijs 的更改是必要的,但此方法的正确性也存在其他一些问题。

您实际上只迭代了 1/4 的像素,因为您是按字节步进的,并且循环的上限是width * height而不是bytesPerRow * height.

此外,计算像素总和似乎并不是您想要的。您可以通过在遇到非零值 ( if bind.pointee != 0) 时立即返回 true 来节省一些工作。

(顺便说一句,如果您将大于 255 的值累积到 a 中,Swift 的整数溢出保护实际上会引发异常UInt8。我想您可以使用更大的整数,或者使用 禁用溢出检查sum = sum &+ bind.pointee,但同样,在第一个非清除时打破循环当累加器“翻转”到 0 时,像素将节省一些时间并防止误报。)

这是对我有用的函数版本:

func anythingHere(_ texture: MTLTexture) -> Bool {
    let width = texture.width
    let height = texture.height
    let bytesPerRow = width * 4

    let data = UnsafeMutableRawPointer.allocate(byteCount: bytesPerRow * height, alignment: 4)
    defer {
        data.deallocate()
    }

    let region = MTLRegionMake2D(0, 0, width, height)
    texture.getBytes(data, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
    var bind = data.assumingMemoryBound(to: UInt8.self)

    for _ in 0..<bytesPerRow * height {
        if bind.pointee != 0 {
            return true
        }
        bind = bind.advanced(by: 1)
    }
    return false
}
Run Code Online (Sandbox Code Playgroud)

请记住,在 macOS 上,storageMode纹理的默认值是managed,这意味着它们的内容在 GPU 上修改时不会自动同步回主内存。您必须明确使用 blit 命令编码器来自己同步内容:

let syncEncoder = buffer.makeBlitCommandEncoder()!
syncEncoder.synchronize(resource: texture)
syncEncoder.endEncoding()
Run Code Online (Sandbox Code Playgroud)