Skip to content

Commit bb938b3

Browse files
authored
Add adapter.[[current]] (#1523)
* Add adapter.current Goal: Make `requestDevice()` behave more similarly across scenarios, so developers don't accidentally use it unportably: make "less extreme" scenarios (like `device.destroy()`) look the same as "more extreme" scenarios (like eGPU unplug or TDR). This prevents developers from accidentally writing code that works in less extreme cases but fails in more extreme cases. Approach: Add an internal `adapter.current` flag. If it's false, the adapter cannot create a device. It starts as true and gets set to false on device loss and system state changes. It never changes back to true: the app must call requestAdapter(), which returns new adapter objects (not reusing existing ones). * address comments; "invalidate adapters" -> "mark adapters stale" * spec that this can happen anytime * nits * fix
1 parent a70049a commit bb938b3

1 file changed

Lines changed: 88 additions & 22 deletions

File tree

spec/index.bs

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,22 @@ An [=adapter=] has the following internal slots:
883883

884884
Each adapter limit must be the same or [=limit/better=] than its default value
885885
in [=supported limits=].
886+
887+
: <dfn>\[[current]]</dfn>, of type boolean
888+
::
889+
Indicates whether the adapter is allowed to vend new devices at this time.
890+
Its value may change at any time.
891+
892+
It is initially set to `true` inside {{GPU/requestAdapter()}}.
893+
It becomes `false` inside "[=lose the device=]" and "[=mark adapters stale=]".
894+
Once set to `false`, it cannot become `true` again.
895+
896+
Note:
897+
This mechanism ensures that various adapter-creation scenarios look similar to applications,
898+
so they can easily be robust to more scenarios with less testing: first initialization,
899+
reinitialization due to an unplugged adapter, reinitialization due to a test
900+
{{GPUDevice/destroy()|GPUDevice.destroy()}} call, etc. It also ensures applications use
901+
the latest system state to make decisions about which adapter to use.
886902
</dl>
887903

888904
[=Adapters=] are exposed via {{GPUAdapter}}.
@@ -894,7 +910,7 @@ through which [=internal objects=] are created.
894910
It can be shared across multiple [=agents=] (e.g. dedicated workers).
895911

896912
A [=device=] is the exclusive owner of all [=internal objects=] created from it:
897-
when the [=device=] is lost, it and all objects created on it (directly, e.g.
913+
when the [=device=] is [=lose the device|lost=], it and all objects created on it (directly, e.g.
898914
{{GPUDevice/createTexture()}}, or indirectly, e.g. {{GPUTexture/createView()}}) become
899915
[=invalid=].
900916

@@ -935,6 +951,22 @@ and adds extra `[[`/`]]` in spec text. Consider removing the brackets.
935951
member corresponding to |key| in |device|.{{device/[[limits]]}} to the value |value|.
936952
</div>
937953

954+
Any time the user agent needs to revoke access to a device, it calls
955+
[=lose the device=](device, `undefined`).
956+
957+
<div algorithm>
958+
To <dfn dfn>lose the device</dfn>(|device|, |reason|):
959+
960+
1. Set |device|.{{device/[[adapter]]}}.{{adapter/[[current]]}} to `false`.
961+
1. Issue: explain how to get from |device| to its "primary" {{GPUDevice}}.
962+
1. Resolve {{GPUDevice/lost|GPUDevice.lost}} with a new {{GPUDeviceLostInfo}} with
963+
{{GPUDeviceLostInfo/reason}} set to |reason| and
964+
{{GPUDeviceLostInfo/message}} set to an implementation-defined value.
965+
966+
Note: {{GPUDeviceLostInfo/message}} should not disclose unnecessary user/system
967+
information and should never be parsed by applications.
968+
</div>
969+
938970
[=Devices=] are exposed via {{GPUDevice}}.
939971

940972
## Optional Capabilities ## {#optional-capabilities}
@@ -1225,12 +1257,13 @@ interface GPU {
12251257
1. Let |promise| be [=a new promise=].
12261258
1. Issue the following steps on the [=Device timeline=] of |this|:
12271259
<div class=device-timeline>
1228-
1. If the user agent chooses to return an adapter:
1260+
1. If the user agent chooses to return an adapter, it should:
12291261

1230-
1. The user agent chooses an [=adapter=] |adapter| according to the rules in
1262+
1. Create an [=adapter=] |adapter| with {{adapter/[[current]]}} set to `true`,
1263+
chosen according to the rules in
12311264
[[#adapter-selection]] and the criteria in |options|.
12321265

1233-
1. |promise| [=resolves=] with a new {{GPUAdapter}} encapsulating |adapter|.
1266+
1. [=Resolve=] |promise| with a new {{GPUAdapter}} encapsulating |adapter|.
12341267

12351268
1. Otherwise, |promise| [=resolves=] with `null`.
12361269
</div>
@@ -1241,6 +1274,38 @@ interface GPU {
12411274
</div>
12421275
</dl>
12431276

1277+
{{GPU}} has the following internal slots:
1278+
1279+
<dl dfn-type=attribute dfn-for=GPU>
1280+
: <dfn>\[[previously_returned_adapters]]</dfn>, of type [=ordered set=]&lt;[=adapter=]&gt;
1281+
::
1282+
The set of [=adapters=] that have been returned via {{GPU/requestAdapter()}}.
1283+
It is used, then cleared, in [=mark adapters stale=].
1284+
</dl>
1285+
1286+
Upon any change in the system's state that could affect the result of any {{GPU/requestAdapter()}}
1287+
call, the user agent *should* [=mark adapters stale=]. For example:
1288+
1289+
- A physical adapter is added/removed (via plug, driver update, TDR, etc.)
1290+
- The system's power configuration has changed (laptop unplugged, power settings changed, etc.)
1291+
1292+
Additionally, [=mark adapters stale=] may by scheduled at any time.
1293+
User agents may choose to do this often even when there has been no system state change (e.g.
1294+
several seconds after the last call to {{GPUAdapter/requestDevice()}}.
1295+
This has no effect on well-formed applications, obfuscates real system state changes, and makes
1296+
developers more aware that calling {{GPU/requestAdapter()}} again is always necessary before
1297+
calling {{GPUAdapter/requestDevice()}}.
1298+
1299+
<div algorithm>
1300+
To <dfn dfn>mark adapters stale</dfn>:
1301+
1302+
1. For each |adapter| in `navigator.gpu.`{{GPU/[[previously_returned_adapters]]}}:
1303+
1. Set |adapter|.{{GPUAdapter/[[adapter]]}}.{{adapter/[[current]]}} to `false`.
1304+
1. [=list/Empty=] `navigator.gpu.`{{GPU/[[previously_returned_adapters]]}}.
1305+
1306+
Issue: Update here if an `adaptersadded`/`adapterschanged` event is introduced.
1307+
</div>
1308+
12441309
<div class="example">
12451310
Request a {{GPUAdapter}}:
12461311
<pre highlight="js">
@@ -1408,16 +1473,22 @@ interface GPUAdapter {
14081473
|adapter|.{{adapter/[[limits]]}}.
14091474
</div>
14101475

1411-
1. If the user agent cannot fulfill the request:
1476+
1. If |adapter|.{{adapter/[[current]]}} is `false`,
1477+
or the user agent otherwise cannot fulfill the request:
1478+
1479+
1. Let |device| be a new [=device=].
1480+
1. [=Lose the device=](|device|, `undefined`).
14121481

1413-
1. Let |device| be a new {{GPUDevice}} object which has
1414-
{{GPUDevice/lost|GPUDevice.lost}} resolved with a {{GPUDeviceLostInfo}}
1415-
with {{GPUDeviceLostInfo/reason}} `undefined` and an
1416-
implementation-defined {{GPUDeviceLostInfo/message}}.
1482+
Note:
1483+
This makes |adapter|.{{adapter/[[current]]}} `false`, if it wasn't already.
14171484

1418-
Issue: Probably centralize this better with other device loss triggering, once added.
1485+
Note:
1486+
User agents should consider issuing developer-visible warnings in
1487+
most or all cases when this occurs. Applications should perform
1488+
reinitialization logic starting with {{GPU/requestAdapter()}}.
14191489

1420-
1. [=Resolve=] |promise| with |device| and stop.
1490+
1. [=Resolve=] |promise| with a new {{GPUDevice}} encapsulating |device|,
1491+
and stop.
14211492

14221493
1. [=Resolve=] |promise| with a new {{GPUDevice}} object encapsulating
14231494
[=a new device=] with the capabilities described by |descriptor|.
@@ -1548,24 +1619,19 @@ Those not defined here are defined elsewhere in this document.
15481619
<dl dfn-type=method dfn-for=GPUDevice>
15491620
: <dfn>destroy()</dfn>
15501621
::
1551-
Destroys the [=device=].
1622+
Destroys the [=device=], preventing further operations on it.
1623+
Outstanding asynchronous operations will fail.
15521624

15531625
<div algorithm=GPUDevice.destroy()>
15541626
**Called on:** {{GPUDevice}} |this|.
15551627

1556-
1. Make |this|.{{GPUDevice/[[device]]}} [=invalid=].
1557-
1. Resolve {{GPUDevice/lost}}, on every {{GPUDevice}} associated with
1558-
|this|.{{GPUDevice/[[device]]}}, with a {{GPUDeviceLostInfo}} with
1559-
{{GPUDeviceLostInfo/reason}} {{GPUDeviceLostReason/"destroyed"}}
1560-
and an implementation-defined {{GPUDeviceLostInfo/message}}.
1561-
1562-
Issue: Probably centralize this better with other device loss triggering, once added.
1628+
1. [=Lose the device=](|this|.{{GPUDevice/[[device]]}},
1629+
{{GPUDeviceLostReason/"destroyed"}}).
15631630
</div>
15641631

15651632
Note:
1566-
This prevents any further operations on the device.
1567-
Implementations can free resource allocations immediately.
1568-
Outstanding asynchronous operations will fail, so implementations can abort them early.
1633+
Since no further operations can occur on this device, implementations can free resource
1634+
allocations and abort outstanding asynchronous operations immediately.
15691635
</dl>
15701636

15711637
{{GPUDevice}} objects are [=serializable objects=].

0 commit comments

Comments
 (0)