wgpu-py icon indicating copy to clipboard operation
wgpu-py copied to clipboard

First class async support

Open almarklein opened this issue 2 years ago • 2 comments

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 createRenderPipelineAsync and mapAsync?
  • Maybe prefix the sync variants with _sync to 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.

almarklein avatar Oct 23 '23 21:10 almarklein

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.

Korijn avatar Oct 27 '23 09:10 Korijn

Proposed approach

  • In wgpu-py support both the sync and async flavor. Use _sync and _async suffixes 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 use loop.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.

almarklein avatar Mar 06 '24 15:03 almarklein