Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

280
Visualizações
Synchronize work between CPU and GPU within single command buffer using MTLSharedEvent

I am trying to use MTLSharedEvent along with MTLSharedEventListener to synchronize computation between GPU and CPU, as in example provided by Apple (https://developer.apple.com/documentation/metal/synchronization/synchronizing_events_between_a_gpu_and_the_cpu). Basically what I want to achieve is have work split into 3 parts executed in order, like so:

  1. GPU computation part 1
  2. CPU computation based on results from GPU computation part 1
  3. GPU computation part 2 after CPU computation

My problem is that eventListener block is always called before command buffer is being scheduled for execution, which make my CPU task execute first in order.

To simplify the case, let’s use simple commands that fill MTLBuffer with certain values (my original use case is more complicated, as using compute encoders with custom shaders, but behaves the same):

let device = MTLCreateSystemDefaultDevice()!
let queue = device.makeCommandQueue()!
let event = device.makeSharedEvent()!
let dispatchQueue = DispatchQueue(label: "myqueue")
let eventListener = MTLSharedEventListener(dispatchQueue: dispatchQueue)

let metalBuffer = device.makeBuffer(length: 2048, options: MTLResourceOptions.storageModeShared)!

let buffer = queue.makeCommandBuffer()!

NSLog("Start - signaled value: \(event.signaledValue)")

event.notify(eventListener, atValue: 1) { event, value in
    // CPU work 
    let pointer = metalBuffer.contents().assumingMemoryBound(to: UInt8.self)
    for i in 0..<512 {
        (pointer + i).pointee = (pointer + i).pointee  + 1;
    }

    NSLog("Event notification - signaled value: \(value), buffer status: \(buffer.status.rawValue)")
    event.signaledValue = 2
}

// GPU work part 1
let encoder1 = buffer.makeBlitCommandEncoder()!
encoder1.fill(buffer: metalBuffer, range: .init(0...127), value: 22)
encoder1.endEncoding()

// signal with 1 to start CPU task 
buffer.encodeSignalEvent(event, value: 1)
// wait for value >= 2 to proceed
buffer.encodeWaitForEvent(event, value: 2)

// GPU work part 2
let encoder2 = buffer.makeBlitCommandEncoder()!
encoder2.fill(buffer: metalBuffer, range: .init(128...511), value: 255)
encoder2.endEncoding()

buffer.addScheduledHandler { buffer in
    NSLog("Buffer scheduled - signaled value: \(event.signaledValue)")
}
buffer.addCompletedHandler { buffer in
    NSLog("Buffer completed - signaled value: \(event.signaledValue)")
}

buffer.commit()
buffer.waitUntilCompleted()

Output:

2022-01-09 23:46:08.774 Sync[76882:3531755] Metal GPU Frame Capture Enabled
2022-01-09 23:46:08.805 Sync[76882:3531755] Start - signaled value: 0
2022-01-09 23:46:08.808 Sync[76882:3531764] Event notification - signaled value: 1, buffer status: 2 (Commited)
2022-01-09 23:46:08.809 Sync[76882:3531763] Buffer scheduled - signaled value: 2
2022-01-09 23:46:08.809 Sync[76882:3531763] Buffer completed - signaled value: 2

As you can see eventListener logs buffer status as .commited. What’s the matter here? Am I missing something?

System: macOS 12.0.1, Apple M1 Pro, Xcode 13.2.1

over 4 years ago · Santiago Trujillo
1 Respostas
Responde à pergunta

0

This is perfectly fine that command buffer is committed. In fact if it wouldn't be committed you'll never get to notify block.

GPU and CPU runs in parallel. So when you use MTLEvent you don't stop executing CPU code (all the Swift code actually). You just tell GPU in what order to execute GPU code.

So what's happening in your case:

  1. All your code runs in a single CPU thread without any interruption.
  2. GPU starts executing command buffer commands only when you call commit(). Before it GPU actually don't do anything. You just scheduled command to be performed on GPU but don't perform them.
  3. When GPU executes commands it checks for your MTLEvent. It performs part 1, then encodes value 1 to event, performs notify block, encodes value 2, performs second GPU block.

But again all the actual GPU work starts only when you call commit() on command buffer. That's why buffer is already committed in notify block. Because it is performed after commit().

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda