Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronous alternatives to functions that return a Future. #3509

Closed
codename-irvin opened this issue Feb 20, 2023 · 10 comments
Closed

Synchronous alternatives to functions that return a Future. #3509

codename-irvin opened this issue Feb 20, 2023 · 10 comments

Comments

@codename-irvin
Copy link

Ideally, functions like Instance::request_adapter, Adapter::request_device, etc. should have alternatives i.e. Instance::request_adapter_sync, Adapter::.request_device_sync, etc. that resolve immediately without returning a future. Using the asynchronous version that currently exists is annoying when only targeting desktop and involves having to either use a third-party library like pollster to wait for the Future, write the equivalent code ourselves to synchronously resolve the Future, or introduce a runtime like Tokio.

The WebGPU spec states that the following functions return Promises:

GPU.requestAdapter()
GPUAdapter.requestDevice()
GPUAdapter.requestAdapterInfo()
GPUDevice.createComputePipelineAsync()
GPUDevice.createRenderPipelineAsync()
GPUBuffer.mapAsync()
GPUShaderModule.compilationInfo()
GPUQueue.onSubmittedWorkDone()
GPUDevice.lost()
GPUDevice.popErrorScope()

Having the corresponding synchronous versions of these functions would certainly be nice.

@grovesNL
Copy link
Collaborator

Hi! 👋 For now if you want to block on these functions we'd recommend something like pollster as you mentioned. I don't think there's much interest in providing synchronous alternatives at the API level right now.

@codename-irvin
Copy link
Author

Pollster definitely works. However, I'd be shocked if others wouldn't want synchronous versions of these functions. In fact, if you look at the implementation of Instance::request_adapter in wpgu_core it isn't even an async function. So using pollster just adds an unnecessary dependency and unnecessary overhead for what could just be a simple synchronous function call.

@codename-irvin
Copy link
Author

In fact, I'd argue that the only reason anyone would use Pollster is because they explicitly do not want to be using async versions of these functions. If they did want the async functionality they'd use something like Tokio.

@grovesNL
Copy link
Collaborator

Right, this is meant to be an implementation detail for now. The overhead of going through pollster should be very low for request_adapter and request_device.

Some of the other native functions are asynchronous and use callbacks internally (triggered by some GPU state change), so it would be good to avoid blocking on some of these functions in general.

@codename-irvin
Copy link
Author

Fair enough. I agree that the overhead is minimal. Coming from JavaScript/Node.js hell I'm just a little wary of the code base being littered with async code. Most developers come to Rust to avoid that sort of thing :).

@teoxoy
Copy link
Member

teoxoy commented Feb 22, 2023

Looking at the WebGPU spec:

  • GPUDevice.createComputePipelineAsync() and GPUDevice.createRenderPipelineAsync() already have a sync variant.

  •  GPU.requestAdapter()
     GPUAdapter.requestDevice()
     GPUShaderModule.compilationInfo()
     GPUDevice.popErrorScope()

    seem to only be async because synchronization is required between the content and device timelines.

  • GPUAdapter.requestAdapterInfo() is async only because the UA might ask the user for permission.

  • GPUDevice.lost() is inherently async (I don't see any advantages to make this sync).

  • GPUBuffer.mapAsync() and GPUQueue.onSubmittedWorkDone() are inherently async; I would track mapSync on Workers - and possibly on the main thread gpuweb/gpuweb#2217 for those.

Conclusion

In our implementation we could add sync variants for:

GPU.requestAdapter()
GPUAdapter.requestDevice()
GPUShaderModule.compilationInfo()
GPUDevice.popErrorScope()
GPUAdapter.requestAdapterInfo()

and possibly for mapAsync, onSubmittedWorkDone and lost but those are controversial.

Issue

The main issue is that to add even the easy ones (requestAdapter, etc), we'd have to polyfill those when targeting WebGPU directly and depending on how the polyfill is implemented (will it block?), it might not be a satisfactory solution.

@daxpedda
Copy link
Contributor

Some previous discussion: #2970.

@grovesNL
Copy link
Collaborator

The main issue is that to add even the easy ones (requestAdapter, etc), we'd have to polyfill those when targeting WebGPU directly and depending on how the polyfill is implemented (will it block?), it might not be a satisfactory solution.

It's not easily possible to polyfill them when targeting WebGPU because we have to yield to the browser's runtime and not block (i.e., block_on will panic). There is a workaround that wasm transformations (e.g., Asyncify https://web.dev/asyncify/#usage-from-other-languages) but it's a wasm/compiler-level transformation.

@codename-irvin
Copy link
Author

As far as I am concerned, the synchronous versions aren't even need when targeting the web. Only enable them for native (non-web) platforms. For folks that want to target both native and web platforms they can either use the async versions or add a few compile time checks to handle both cases.

@teoxoy
Copy link
Member

teoxoy commented Feb 22, 2023

There is a workaround that wasm transformations (e.g., Asyncify https://web.dev/asyncify/#usage-from-other-languages) but it's a wasm/compiler-level transformation.

This sounds like what I had in mind where the WebAssembly runtime will inject some extra machinery to make the async call look like a sync one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants