First class async support
Introduction
WebGPU defines certain functions and props as async, so that in an async setting, there is less wait time. Would make a lot of sense to also support that in wgpu-py.
Would be great if we can make this work without forcing asyncio, but also supporting e.g. trio.
At the moment the async API of wgpu-native is a bit crude (there is a function to poll its loop). Last I heard was that this API will change at some point. So might be worth waiting for that.
In our code, all async methods have an _async suffix, and they all have a sync version of the function (that simply waits).
Aync parts in WebGPU spec
Searching the IDL for "Promise" results in the following list (05-03-2024):
-
gpu.requestAdapter() -
adapter.requestDevice() -
adapter.requestAdapterInfo() -
device.createComputePipelineAsync() -
device.createRenderPipelineAsync() -
buffer.mapAsync() -
shader.getCompilationInfo() -
queue.onSubmittedWorkDone() -
device.lost -
device.popErrorScope()
Considerations
- Would be nice to be able to use the API synchronously, e.g. make a script that does compute.
- wgpu (and derived code) should work well in an interactive session.
- Also support e.g. Trio.
- Not all GUI event loops support async/await, e.g. Qt.
- For WebGPU: no way to make the async functions synchronous (I think).
- How much is performance afected by using sync versions of
createRenderPipelineAsyncandmapAsync? - Maybe prefix the sync variants with
_syncto indicate that an async variant is available?
Thoughts
I think I'd like to try making wgpu-py async according to the spec, dropping the sync versions. Let's see how far we can take that. In render engines, one could run "draw iteration" on asyncio, if necessary with loop.run_until_complete(). Also when inside Qt.
If we can make that work, we're more compliant, avoid busy waiting in user code, and remove the biggest hurlde to move to the browser.
Clarification: Right now we've implemented all async functions with polling, in a blocking fashion.
It still needs to be investigated if the API exposed by wgpu-native enables a true async implementation in wgpu-py.
Proposed approach
- In wgpu-py support both the sync and async flavor. Use
_syncand_asyncsuffixes for clarity. - In pygfx require the
animate()function to be a co-routine, so we can use the async variant all the way. - Maybe also allow sync version if creating a sync and async path is viable and worth the effort.
- In event-loops that don't support async/await, like wx and (older) Qt, the
_draw_frame_and_present()method can useloop.run_until_complete(). This is the trickiest part of this proposal (e.g. what happens if the user schedules new asyncio tasks).
We should probably just try and see how far we get.