简体   繁体   中英

Detecting if a MTLTexture is blank

I am trying to determine if a MTLTexture (in bgra8Unorm format) is blank by calculating the sum of all the RGB and A components of each of its pixels.

This function intends to do this by adding adjacent floats in memory after a texture has been copied to a pointer. However I have determined that this function ends up returning false nomatter the MTLTexture given.

What is wrong with this function?

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' change is necessary, but there are also a couple of other issues with the correctness of this method.

You're actually only iterating over 1/4 of the pixels, since you're stepping byte-wise and the upper bound of your loop is width * height rather than bytesPerRow * height .

Additionally, computing the sum of the pixels doesn't really seem like what you want. You can save some work by returning true as soon as you encounter a non-zero value ( if bind.pointee != 0 ).

(Incidentally, Swift's integer overflow protection will actually raise an exception if you accumulate a value greater than 255 into a UInt8 . I suppose you could use a bigger integer, or disable overflow checking with sum = sum &+ bind.pointee , but again, breaking the loop on the first non-clear pixel will save some time and prevent false positives when the accumulator "rolls over" to exactly 0.)

Here's a version of your function that worked for me:

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
}

Keep in mind that on macOS, the default storageMode for textures is managed , which means their contents aren't automatically synchronized back to main memory when they're modified on the GPU. You must explicitly use a blit command encoder to sync the contents yourself:

let syncEncoder = buffer.makeBlitCommandEncoder()!
syncEncoder.synchronize(resource: texture)
syncEncoder.endEncoding()

Didn't look in detail at the rest of the code, but I think this,

bind.advanced(by: 1)

should be:

bind = bind.advanced(by: 1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM