No working Buffer.MapAsync functions
Similar to #15 , I'm struggling to use mapAsync() in order to read a buffer back
I finally got the swift syntax correct,
var callback : (BufferMapAsyncStatus) -> Void =
{
status in
print("Status \(status)")
IsFinished = status == .success
}
try readBuffer.mapAsync(mode: .read, offset: 0, size: Int(outputByteSize), callback: callback)
but all I get is
Warning: Old MapAsync APIs are deprecated. If using C please pass a CallbackInfo struct that has two userdatas. Otherwise, if using C++, please use templated helpers.
and a failure (it doesn't throw, but the thread stops, not sure what swift is doing there - unless the function is just blocking and never returns...)
- Is there a different function I'm supposed to use?
- Is this like #15 and the swift bindings generator missing some cases?
- Am I just calling the wrong functons to map a buffer to read back to the CPU....
- Thinking about it, the alternatives have
WGPUFuturewhich the linked bug in #15 dawn says, isn't implemented... so now I'm not sure which is the correct function...
My mistake, .mapAsync() runs, I get the warning - but the callback never gets invoked, and all my calls like getConstMappedRange() afterwards always return nil
I've checked the dawn code, something still runs (In theory - i should compile a debug version of the dylib...), but just not getting a callback
Aha! After trying to find some buffer map & read examples, i came across this https://eliemichel.github.io/LearnWebGPU/basic-3d-rendering/input-geometry/playing-with-buffers.html#asynchronous-polling
If you run the program at this point, you might be surprised (and disappointed) to see that the callback is never executed! We saw this in the Device Polling section of the Command Queue chapter: there is no hidden process executed by the WebGPU library to check that the async operation is ready so we must do it ourselves.
Unfortunately, this mechanism has no standard solution yet, so we write it differently for Dawn, wgpu-native and emscripten, with a little subtlety for the latter:
Thus, I added a device.tick() func, after my call to mapAsync() (the one in the current code/generated) - and I got my callback!
-
let state = readBuffer.mapStatealso has the correct state
// Extensions.swift
extension Device
{
public func tick()
{
withUnsafeHandle {
handle in
wgpuDeviceTick(handle)
}
}
}
I know there has been a lot of discussion and movement around the async methods. It sounds like things have now matured if you're getting a deprecation message. I can take a look at this.
However, in the meantime, I suspect if you call instance.processEvents() periodically (e.g. once per frame), you should find that the callbacks get processed.
It was originally designed for javascript, where I suspect processEvents gets called as part of the JS event loop. I believe the new design allows truly spontaneous callbacks, although this might need some caution as they might be called on a different thread.
Yes, device.tick() probably does the same thing essentially. I would encourage you to use processEvents() though as this is the more standardised method moving forward.
There's some details on where things are headed here.
instance.processEvents() also works!
Working well!
Running a kernel to do rgb -> bgra conversion, reading the buffer & sending the output over to a virtual webcam, and putting that buffer into a texture, to render with swiftui! :)
https://bsky.app/profile/soylentgraham.bsky.social/post/3lahzok5jqs2h
Now, to close this issue - I think I'm going to submit an extension that akin to the below (because the callbacks are also a bit frustrating to write)
await buffer.mapAsync() throws
{
mapasync()
//wait for callback/status
// tick/process explicitly
}