我试图通过计算每个像素的所有 R G B 和 A 分量的总和来确定 MTLTexture(bgra8Unorm 格式)是否为空白。
此函数旨在通过在将纹理复制到指针后在内存中添加相邻的 float 来执行此操作。但是我已经确定这个函数最终会返回 false nomatter 给定的 MTLTexture。
这个函数有什么问题?
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
}
最佳答案
Matthijs 的改变是必要的,但这种方法的正确性还存在一些其他问题。
您实际上只迭代了 1/4 的像素,因为您是按字节步进并且循环的上限是 width * height
而不是 bytesPerRow * height
.
此外,计算像素总和并不是您真正想要的。您可以通过在遇到非零值 (if bind.pointee != 0
) 时立即返回 true 来节省一些工作。
(顺便说一句,如果您将大于 255 的值累积到 UInt8
中,Swift 的整数溢出保护实际上会引发异常。我想您可以使用更大的整数,或者使用 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
}
请记住,在 macOS 上,默认 storageMode
纹理是 managed
,这意味着当它们在 GPU 上被修改时,它们的内容不会自动同步回主内存。您必须明确使用 blit 命令编码器来自己同步内容:
let syncEncoder = buffer.makeBlitCommandEncoder()!
syncEncoder.synchronize(resource: texture)
syncEncoder.endEncoding()
关于ios - 检测 MTLTexture 是否为空白,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49375701/