sarasvati
sarasvati

Reputation: 792

iOS Metal – reading old values while writing into texture

I have a kernel function (compute shader) that reads nearby pixels of a pixel from a texture and based on the old nearby-pixel values updates the value of the current pixel (it's not a simple convolution).

I've tried creating a copy of the texture using BlitCommandEncoder and feeding the kernel function with 2 textures - one read-only and another write-only. Unfortunately, this approach is GPU-wise time consuming.

What is the most efficient (GPU- and memory-wise) way of reading old values from a texture while updating its content?

Upvotes: 1

Views: 1232

Answers (2)

ldoogy
ldoogy

Reputation: 2869

I have written plenty of iOS Metal code that samples (or reads) from the same texture it is rendering into. I am using the render pipeline, setting my texture as the render target attachment, and also loading it as a source texture. It works just fine.

To be clear, a more efficient approach is to use the color() attribute in your fragment shader, but that is only suitable if all you need is the value of the current fragment, not any other nearby positions. If you need to read from other positions in the render target, I would just load the render target as a source texture into the fragment shader.

Upvotes: 0

infinisil
infinisil

Reputation: 1876

(Bit late but oh well)

There is no way you could make it work with only one texture, because the GPU is a highly parallel processor: Your kernel that you wrote for a single pixel gets called in parallel on all pixels, you can't tell which one goes first.

So you definitely need 2 textures. The way you probably should do it is by using 2 textures where one is the "old" one and the other the "new" one. Between passes, you switch the role of the textures, now old is new and new is old. Here is some pseudoswift:

var currentText = MTLTexture()
var nextText = MTLTexture()

let semaphore = dispatch_semaphore_create(1)

func update() {
    dispatch_semaphore_wait(semaphore) // Wait for updating done signal

    let commands = commandQueue.commandBuffer()
    let encoder = commands.computeCommandEncoder()

    encoder.setTexture(currentText, atIndex: 0)
    encoder.setTexture(nextText, atIndex: 1)

    encoder.dispatchThreadgroups(...)
    encoder.endEncoding()

    // When updating done, swap the textures and signal that it's done updating
    commands.addCompletionHandler { 
        swap(&currentText, &nextText)
        dispatch_semaphore_signal(semaphore)
    }
    commands.commit()
}

Upvotes: 3

Related Questions