-
Notifications
You must be signed in to change notification settings - Fork 386
requestDevice returns null if adapter is lost #521
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,7 +39,8 @@ partial interface GPUDevice { | |
| `GPUAdapter.requestDevice` requests a device from the adapter. | ||
| It returns a Promise which resolves when a device is ready. | ||
| The Promise may not resolve for a long time - it resolves when the browser is ready for the application to bring up (or restore) its content. | ||
| If the adapter is unable to create a device (i.e. because the adapter was lost), the Promise rejects. | ||
| If the adapter is lost and therefore unable to create a device, `requestDevice()` returns null. | ||
| If the `options` are invalid (e.g. they exceed the limits of the adapter), `requestDevice()` rejects. | ||
|
|
||
| The `GPUDevice` may be lost if something goes fatally wrong on the device (e.g. unexpected driver error, crash, or native device loss). | ||
| The `GPUDevice` provides a promise, `device.lost`, which resolves when the device is lost. | ||
|
|
@@ -50,6 +51,11 @@ The device and all objects created from the device have become invalid. | |
| All further operations on the device and its objects are errors. | ||
| The `"validationerror"` event will no longer fire. (This makes all further operations no-ops.) | ||
|
|
||
| An app should never give up on getting WebGPU access due to | ||
| `requestDevice` returning null or `GPUDevice.lost` resolving. | ||
| It should only give up based on a `requestAdapter` rejection. | ||
| (It should also give up on a `requestDevice` rejection, as that indicates an app programming error.) | ||
|
|
||
| ### Example Code | ||
|
|
||
| ```js | ||
|
|
@@ -66,47 +72,43 @@ class MyRenderer { | |
| this.initFallback(); | ||
| } | ||
| } | ||
| async initWebGPU() { | ||
| await this.ensureDevice(); | ||
| // ... Upload resources, etc. | ||
| } | ||
| initFallback() { /* try WebGL, 2D Canvas, or other fallback */ } | ||
| async ensureDevice() { | ||
| async initWebGPU() { | ||
| // Stop rendering. (If there was already a device, WebGPU calls made before | ||
| // the app notices the device is lost are okay - they are no-ops.) | ||
| this.device = null; | ||
|
|
||
| // Keep current adapter (but make a new one if there isn't a current one.) | ||
| // If we can't get an adapter, ensureDevice rejects and the app falls back. | ||
| await ensureAdapter(); | ||
|
|
||
| try { | ||
| await ensureDeviceOnCurrentAdapter(); | ||
| // Got a device. | ||
| return; | ||
| } catch (e) { | ||
| console.error("device request failed", e); | ||
| // That failed; try a new adapter entirely. | ||
| await tryEnsureDeviceOnCurrentAdapter(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this call to tryEnsureDeviceOnCurrentAdapter() can be folded inside the loop.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first one tries to reuse the current adapter while the later ones get a new adapter, though maybe this is wrong. I think this code still needs some revision. |
||
| // If the device is null, the adapter was lost. Try a new adapter. | ||
| // Continue doing this until one is found or an error is thrown. | ||
| while (!this.device) { | ||
| this.adapter = null; | ||
| // If we can't get a new adapter, it causes ensureDevice to reject and the app to fall back. | ||
| await ensureAdapter(); | ||
| await ensureDeviceOnCurrentAdapter(); | ||
| await tryEnsureDeviceOnCurrentAdapter(); | ||
| } | ||
|
|
||
| // ... Upload resources, etc. | ||
| } | ||
| async ensureAdapter() { | ||
| async tryEnsureDeviceOnCurrentAdapter() { | ||
| // If no adapter, get one. | ||
| // If we can't, rejects and the app falls back. | ||
| if (!this.adapter) { | ||
| // If no adapter, get one. | ||
| // (If requestAdapter rejects, no matching adapter is available. Exit to fallback.) | ||
| this.adapter = await gpu.requestAdapter({ /* options */ }); | ||
| } | ||
| } | ||
| async ensureDeviceOnCurrentAdapter() { | ||
|
|
||
| // Try to get a device. | ||
| // null => try new adapter | ||
| // rejection => options were invalid (app programming error) | ||
| this.device = await this.adapter.requestDevice({ /* options */ }); | ||
| this.device.lost.then((info) => { | ||
| // Device was lost. | ||
| console.error("device lost", info); | ||
| // Try to get a device again. | ||
| this.ensureDevice(); | ||
| if (!this.device) { | ||
| return; | ||
| } | ||
| // When the device is lost, just try to get a device again. | ||
| device.lost.then((info) => { | ||
| console.error("Device was lost.", info); | ||
| this.initWebGPU(); | ||
| }); | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels to me that "should give up" and "shouldn't give up" distinction is too high level. We could maybe specify it in terms of "this is a function of Xxx", which implies to the user whether they should try again or not. I.e. we can say that the rejection is deterministic based on the adapter capabilities and the provided options. This is better than "should give up" because it also allows for a case where the user probes for different limits, so they would actually want to try again with different options.
At the same time, we'd say that returning
nullis an indication of a temporary state that doesn't depend on the parameters.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(this is just the design doc, so we can make it more formal in spec)
requestDevice rejects if you ask for higher limits than its adapter has. Apps shouldn't be doing that (they should check the adapter limits instead) making this rejection an app programming error.
requestAdapter rejects if no adapters could possibly be returned. Given the only adapter request option right now is powerPreference, the options should never have an impact on whether or not you get an adapter. Perhaps in the future it would though.