Repository navigation
Expand file tree
/
Copy pathindex.bs
More file actions
8680 lines (6900 loc) · 371 KB
/
Copy pathindex.bs
File metadata and controls
8680 lines (6900 loc) · 371 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<pre class='metadata'>
Title: WebGPU
Shortname: webgpu
Level: 1
Status: w3c/ED
Group: webgpu
URL: https://gpuweb.github.io/gpuweb/
!Participate: <a href="https://github.com/gpuweb/gpuweb/issues/new">File an issue</a> (<a href="https://github.com/gpuweb/gpuweb/issues">open issues</a>)
Editor: Dzmitry Malyshau, Mozilla https://www.mozilla.org, dmalyshau@mozilla.com
Editor: Kai Ninomiya, Google https://www.google.com, kainino@google.com
Former Editor: Justin Fan, Apple https://www.apple.com, justin_fan@apple.com
Abstract: WebGPU exposes an API for performing operations, such as rendering and computation, on a Graphics Processing Unit.
Markup Shorthands: markdown yes
Markup Shorthands: dfn yes
Markup Shorthands: idl yes
Markup Shorthands: css no
Assume Explicit For: yes
</pre>
<pre class="link-defaults">
spec:html;
type:interface; text:Navigator
</pre>
<pre class='anchors'>
spec: ECMA-262; urlPrefix: https://tc39.github.io/ecma262/#
type: dfn
text: agent; url: agent
text: surrounding agent; url: surrounding-agent
text: agent cluster; url: sec-agent-clusters
spec: webidl; urlPrefix: https://heycam.github.io/webidl/#
type: dfn
text: resolve; url: resolve
spec: web-apis; urlPrefix: https://html.spec.whatwg.org/multipage/webappapis.html#
type: dfn
text: cross-origin isolated; url: cross-origin-isolated
</pre>
<style>
/* Make <dl> blocks more distinct from their surroundings. */
dl:not(.switch) {
border-left: thin solid #f3e48c;
padding-left: .5em;
}
/* <p> by default has these margins. Update ul/ol/dl to match,
* since they are also put in places where paragraphs go. */
p, ul, ol, dl {
margin: 1em 0;
}
/* Box for Valid Usage requirements. */
div.validusage {
padding: .5em;
border: thin solid #88e !important;
border-radius: .5em;
}
/*
* If the Valid Usage requirements are the first child of a *-timeline block give it a larger top
* margin to prevent the block labels from overlapping.
*/
.content-timeline>.validusage:first-child,
.device-timeline>.validusage:first-child,
.queue-timeline>.validusage:first-child {
margin-top: 1.5em;
}
/*
* Boxes for steps that occur on a particular timeline.
*/
div.content-timeline, div.device-timeline, div.queue-timeline {
padding: .5em;
border-radius: .5em;
}
.content-timeline {
background: rgba(0, 255, 0, 0.05);
background: var(--tint-green);
}
.device-timeline {
background: rgba(255, 0, 0, 0.05);
background: var(--tint-red);
}
.queue-timeline {
background: rgba(255, 0, 255, 0.05);
background: var(--tint-purple);
}
/*
* Stylistic labels, for clarity of presentation of these blocks.
*
* NOTE: This text is non-accessible and non-selectable; surrounding
* text must also explain the context.
*/
.validusage, .content-timeline, .device-timeline, .queue-timeline {
position: relative;
}
.validusage::before,
.content-timeline::before,
.device-timeline::before,
.queue-timeline::before {
font-weight: bold;
font-style: italic;
font-size: 130%;
color: rgba(0, 0, 0, 0.15);
color: var(--watermark-text);
position: absolute;
right: .3em;
top: -.1em;
}
.validusage::before {
content: "Valid Usage";
}
.content-timeline::before {
content: "Content Timeline";
}
.device-timeline::before {
content: "Device Timeline";
}
.queue-timeline::before {
content: "Queue Timeline";
}
/*
* Ensure that argumentdef blocks don't overflow algorithm section borders. This is made far harder
* than it needs to be because the top-level W3C stylesheet has several @media + min-width variants
* that mark themselves as !important and then proceed to do the wrong thing.
*/
@media screen and (min-width: 78em) {
body:not(.toc-inline) .algorithm .overlarge {
margin-right: auto !important;
}
}
@media screen and (min-width: 90em) {
body:not(.toc-inline) .algorithm .overlarge {
margin-right: auto !important;
}
}
.algorithm .overlarge {
margin-right: auto !important;
}
/*
* The default algorithm style has a caption that doesn't suit this spec's
* formatting particularly well. Hide it.
*/
.algorithm .argumentdef {
margin-top: 0;
}
.algorithm .argumentdef>caption {
display: none;
}
/*
* Add vertical lines to demarcate multi-column cells.
*/
table.data td[colspan] {
border-left-style: dotted;
border-right-style: dotted;
}
table.data.no-colspan-center td[colspan],
table.data.no-colspan-center th[colspan] {
text-align: unset;
}
table.data tr.row-continuation td,
table.data tr.row-continuation th {
border-top: none;
}
/*
* Sticky table headers.
*/
.overlarge {
/* position: sticky doesn't work inside scrollable elements. */
overflow-x: unset;
}
thead.stickyheader th, th.stickyheader {
position: sticky;
top: 0;
background: #f8f8f8;
background: var(--stickyheader-background);
}
/*
* Darkmode colors
*/
:root {
--watermark-text: rgba(0, 0, 0, 15%);
--stickyheader-background: #f8f8f8;
--tint-red: rgba(255, 0, 0, 6%);
--tint-green: rgba(0, 255, 0, 10%);
--tint-blue: rgba(0, 0, 255, 5%);
--tint-purple: rgba(255, 0, 255, 5%);
}
@media (prefers-color-scheme:dark) {
:root {
--watermark-text: rgba(255, 255, 255, 25%);
--stickyheader-background: #181818;
--tint-red: rgba(255, 0, 0, 20%);
--tint-green: rgba(0, 255, 0, 18%);
--tint-blue: rgba(0, 130, 255, 24%);
--tint-purple: rgba(255, 0, 255, 22%);
}
}
</style>
# Introduction # {#intro}
*This section is non-normative.*
[Graphics Processing Units](https://en.wikipedia.org/wiki/Graphics_processing_unit), or GPUs for short,
have been essential in enabling rich rendering and computational applications in personal computing.
WebGPU is an API that exposes the capabilities of GPU hardware for the Web.
The API is designed from the ground up to efficiently map to the
[Vulkan](https://www.khronos.org/vulkan/),
[Direct3D 12](https://docs.microsoft.com/en-us/windows/win32/direct3d12/what-is-directx-12-),
and [Metal](https://developer.apple.com/metal/) native GPU APIs.
WebGPU is not related to [WebGL](https://www.khronos.org/webgl/) and does not explicitly target OpenGL ES.
WebGPU sees physical GPU hardware as {{GPUAdapter}}s. It provides a connection to an adapter via
{{GPUDevice}}, which manages resources, and the device's {{GPUQueue}}s, which execute commands.
{{GPUDevice}} may have its own memory with high-speed access to the processing units.
{{GPUBuffer}} and {{GPUTexture}} are the <dfn dfn>physical resources</dfn> backed by GPU memory.
{{GPUCommandBuffer}} and {{GPURenderBundle}} are containers for user-recorded commands.
{{GPUShaderModule}} contains [=shader=] code. The other resources,
such as {{GPUSampler}} or {{GPUBindGroup}}, configure the way [=physical resources=] are used by the GPU.
GPUs execute commands encoded in {{GPUCommandBuffer}}s by feeding data through a [=pipeline=],
which is a mix of fixed-function and programmable stages. Programmable stages execute
<dfn dfn>shaders</dfn>, which are special programs designed to run on GPU hardware.
Most of the state of a [=pipeline=] is defined by
a {{GPURenderPipeline}} or a {{GPUComputePipeline}} object. The state not included
in these [=pipeline=] objects is set during encoding with commands,
such as {{GPUCommandEncoder/beginRenderPass()}} or {{GPURenderPassEncoder/setBlendConstant()}}.
# Malicious use considerations # {#malicious-use}
*This section is non-normative.* It describes the risks associated with exposing this API on the Web.
## Security ## {#security}
The security requirements for WebGPU are the same as ever for the web, and are likewise non-negotiable.
The general approach is strictly validating all the commands before they reach GPU,
ensuring that a page can only work with its own data.
### CPU-based undefined behavior ### {#security-cpu-ub}
A WebGPU implementation translates the workloads issued by the user into API commands specific
to the target platform. Native APIs specify the valid usage for the commands
(for example, see [vkCreateDescriptorSetLayout](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCreateDescriptorSetLayout.html))
and generally don't guarantee any outcome if the valid usage rules are not followed.
This is called "undefined behavior", and it can be exploited by an attacker to access memory
they don't own, or force the driver to execute arbitrary code.
In order to disallow insecure usage, the range of allowed WebGPU behaviors is defined for any input.
An implementation has to validate all the input from the user and only reach the driver
with the valid workloads. This document specifies all the error conditions and handling semantics.
For example, specifying the same buffer with intersecting ranges in both "source" and "destination"
of {{GPUCommandEncoder/copyBufferToBuffer()}} results in {{GPUCommandEncoder}}
generating an error, and no other operation occurring.
See [[#errors-and-debugging]] for more information about error handling.
## GPU-based undefined behavior ## {#security-gpu-ub}
WebGPU [=shader=]s are executed by the compute units inside GPU hardware. In native APIs,
some of the shader instructions may result in undefined behavior on the GPU.
In order to address that, the shader instruction set and its defined behaviors are
strictly defined by WebGPU. When a shader is provided to {{GPUDevice/createShaderModule()}},
the WebGPU implementation has to validate it
before doing any translation (to platform-specific shaders) or transformation passes.
## Uninitialized data ## {#security-uninitialized}
Generally, allocating new memory may expose the leftover data of other applications running on the system.
In order to address that, WebGPU conceptually initializes all the resources to zero, although in practice
an implementation may skip this step if it sees the developer initializing the contents manually.
This includes variables and shared workgroup memory inside shaders.
The precise mechanism of clearing the workgroup memory can differ between platforms.
If the native API does not provide facilities to clear it, the WebGPU implementation transforms the compute
shader to first do a clear across all invocations, synchronize them, and continue executing developer's code.
## Out-of-bounds access in shaders ## {#security-shader}
[=Shader=]s can access [=physical resource=]s either directly
(for example, as a {{GPUBufferBindingType/"uniform"}} {{GPUBufferBinding}}), or via <dfn dfn>texture unit</dfn>s,
which are fixed-function hardware blocks that handle texture coordinate conversions.
Validation on the API side can only guarantee that all the inputs to the shader are provided and
they have the correct usage and types.
The host API side can not guarantee that the data is accessed within bounds
if the [=texture unit=]s are not involved.
Issue: define the host API distinct from the shader API
In order to prevent the shaders from accessing GPU memory an application doesn't own,
the WebGPU implementation may enable a special mode (called "robust buffer access") in the driver
that guarantees that the access is limited to buffer bounds.
Alternatively, an implementation may transform the shader code by inserting manual bounds checks.
When this path is taken, the out-of-bound checks only apply to array indexing. They aren't needed
for plain field access of shader structures due to the {{GPUBufferBindingLayout/minBindingSize}}
validation on the host side.
If the shader attempts to load data outside of [=physical resource=] bounds,
the implementation is allowed to:
1. return a value at a different location within the resource bounds
2. return a value vector of "(0, 0, 0, X)" with any "X"
3. partially discard the draw or dispatch call
If the shader attempts to write data outside of [=physical resource=] bounds,
the implementation is allowed to:
1. write the value to a different location within the resource bounds
2. discard the write operation
3. partially discard the draw or dispatch call
## Invalid data ## {#security-invalid-data}
When uploading [floating-point](https://en.wikipedia.org/wiki/IEEE_754) data from CPU to GPU,
or generating it on the GPU, we may end up with a binary representation that doesn't correspond
to a valid number, such as infinity or NaN (not-a-number). The GPU behavior in this case is
subject to the accuracy of the GPU hardware implementation of the IEEE-754 standard.
WebGPU guarantees that introducing invalid floating-point numbers would only affect the results
of arithmetic computations and will not have other side effects.
### Driver bugs ### {#security-driver-bugs}
GPU drivers are subject to bugs like any other software. If a bug occurs, an attacker
could possibly exploit the incorrect behavior of the driver to get access to unprivileged data.
In order to reduce the risk, the WebGPU working group will coordinate with GPU vendors
to integrate the WebGPU Conformance Test Suite (CTS) as part of their driver testing process,
like it was done for WebGL.
WebGPU implementations are expected to have workarounds for some of the discovered bugs,
and disable WebGPU on drivers with known bugs that can't be worked around.
### Timing attacks ### {#security-timing}
WebGPU is designed for multi-threaded use via Web Workers. As such, it is designed not to open
the users to modern high-precision timing attacks. Some of the objects,
like {{GPUBuffer}} or {{GPUQueue}}, have shared state which can be simultaneously accessed.
This allows race conditions to occur, similar to those of accessing a `SharedArrayBuffer`
from multiple Web Workers, which makes the thread scheduling observable.
WebGPU addresses this by limiting the ability to deserialize (or share) objects only to
the [=agents=] inside the [=agent cluster=], and only if
the [cross-origin isolated](https://web.dev/coop-coep/) policies are in place.
This restriction matches the [mitigations](https://hacks.mozilla.org/2020/07/safely-reviving-shared-memory/)
against the malicious `SharedArrayBuffer` use. Similarly, the user agent may also
serialize the [=agents=] sharing any handles to prevent any concurrency entirely.
In the end, the attack surface for races on shared state in WebGPU will be
a small subset of the `SharedArrayBuffer` attacks.
WebGPU also specifies the {{GPUFeatureName/"timestamp-query"}} feature, which
provides high precision timing of GPU operations. The feature is optional, and a WebGPU
implementation may limit its exposure only to those scenarios that are trusted. Alternatively,
the timing query results could be processed by a compute shader and aligned to a lower precision.
### Row hammer attacks ### {#security-rowhammer}
[Row hammer](https://en.wikipedia.org/wiki/Row_hammer) is a class of attacks that exploit the
leaking of states in DRAM cells. It could be used [on GPU](https://www.vusec.net/projects/glitch/).
WebGPU does not have any specific mitigations in place, and relies on platform-level solutions,
such as reduced memory refresh intervals.
## Denial of service ## {#security-dos}
WebGPU applications have access to GPU memory and compute units. A WebGPU implementation may limit
the available GPU memory to an application, in order to keep other applications responsive.
For GPU processing time, a WebGPU implementation may set up "watchdog" timer that makes sure an
application doesn't cause GPU unresponsiveness for more than a few seconds.
These measures are similar to those used in WebGL.
## Workload identification ## {#security-workload-identification}
WebGPU provides access to constrained global resources shared between different programs
(and web pages) running on the same machine. An application can try to indirectly probe
how constrained these global resources are, in order to reason about workloads performed
by other open web pages, based on the patterns of usage of these shared resources.
These issues are generally analogous to issues with Javascript,
such as system memory and CPU execution throughput. WebGPU does not provide any additional
mitigations for this.
### Memory resources ### {#security-memory-resources}
WebGPU exposes fallible allocations from machine-global memory heaps, such as VRAM.
This allows for probing the size of the system's remaining available memory
(for a given heap type) by attempting to allocate and watching for allocation failures.
GPUs internally have one or more (typically only two) heaps of memory
shared by all running applications. When a heap is depleted, WebGPU would fail to create a resource.
This is observable, which may allow a malicious application to guess what heaps
are used by other applications, and how much they allocate from them.
### Computation resources ### {#security-computation-resources}
If one site uses WebGPU at the same time as another, it may observe the increase
in time it takes to process some work. For example, if a site constantly submits
compute workloads and tracks completion of work on the queue,
it may observe that something else also started using the GPU.
A GPU has many parts that can be tested independently, such as the arithmetic units,
texture sampling units, atomic units, etc. A malicious application may sense when
some of these units are stressed, and attempt to guess the workload of another
application by analyzing the stress patterns. This is analogous to the realities
of CPU execution of Javascript.
## Privacy ## {#security-privacy}
### Machine-specific limits ### {#security-machine-limits}
WebGPU can expose a lot of detail on the underlying GPU architecture and the device geometry.
This includes available physical adapters, many limits on the GPU and CPU resources
that could be used (such as the maximum texture size), and any optional hardware-specific
capabilities that are available.
User agents are not obligated to expose the real hardware limits, they are in full contol of
how much the machine specifics are exposed. One strategy to reduce fingeprinting is binning
all the target platforms into a few number of bins. In general, the privacy impact of exposing
the hardware limits matches the one of WebGL.
The [=limit/default=] limits are also deliberately high enough
to allow most application to work without requesting higher limits.
All the usage of the API is validated according to the requested limits,
so the actual hardware capabilities are not exposed to the users by accident.
### Machine-specific artifacts ### {#security-machine-artifacts}
There are some machine-specific rasterization/precision artifacts and performance differences
that can be observed roughly in the same way as in WebGL. This applies to rasterization coverage
and patterns, interpolation precision of the varyings between shader stages, compute unit scheduling,
and more aspects of execution.
Generally, rasterization and precision fingerprints are identical across most or all
of the devices of each vendor. Performance differences are relatively intractable,
but also relatively low-signal (as with JS execution performance).
Privacy-critical applications and user agents should utilize software implementations to eliminate such artifacts.
### Machine-specific performance ### {#security-machine-performance}
Another factor for differentiating users is measuring the performance of specific
operations on the GPU. Even with low precision timing, repeated execution of an operation
can show if the user's machine is fast at specific workloads.
This is a fairly common vector (present in both WebGL and Javascript),
but it's also low-signal and relatively intractable to truly normalize.
WebGPU compute pipelines expose access to GPU unobstructed by the fixed-function hardware.
This poses an additional risk for unique device fingerprinting. User agents can take steps
to dissociate logical GPU invocations with actual compute units to reduce this risk.
# Fundamentals # {#fundamentals}
## Conventions ## {#api-conventions}
### Dot Syntax ### {#dot-syntax}
In this specification, the `.` ("dot") syntax, common in programming languages, is used.
The phrasing "`Foo.Bar`" means "the `Bar` member of the value (or interface) `Foo`."
For example, where `buffer` is a {{GPUBuffer}}, `buffer.[[device]].[[adapter]]` means
"the `[[adapter]]` internal slot of the `[[device]]` internal slot of `buffer`.
### Internal Objects ### {#webgpu-internal-objects}
An <dfn dfn>internal object</dfn> is a conceptual, non-exposed WebGPU object.
[=Internal objects=] track the state of an API object and hold any underlying implementation.
If the state of a particular [=internal object=] can change in parallel from multiple [=agents=],
those changes are always atomic with respect to all [=agents=].
Note: An "[=agent=]" refers to a JavaScript "thread" (i.e. main thread, or Web Worker).
### WebGPU Interfaces ### {#webgpu-interfaces}
A <dfn dfn>WebGPU interface</dfn> is an exposed interface which encapsulates an [=internal object=].
It provides the interface through which the [=internal object=]'s state is changed.
As a matter of convention, if a [=WebGPU interface=] is referred to as [=invalid=],
it means that the [=internal object=] it encapsulates is [=invalid=].
Any interface which includes {{GPUObjectBase}} is a [=WebGPU interface=].
<script type=idl>
interface mixin GPUObjectBase {
attribute USVString? label;
};
</script>
{{GPUObjectBase}} has the following attributes:
<dl dfn-type=attribute dfn-for=GPUObjectBase>
: <dfn>label</dfn>
::
A label which can be used by development tools (such as error/warning messages,
browser developer tools, or platform debugging utilities) to identify the underlying
[=internal object=] to the developer.
It has no specified format, and therefore cannot be reliably machine-parsed.
In any given situation, the user agent may or may not choose to use this label.
</dl>
{{GPUObjectBase}} has the following internal slots:
<dl dfn-type=attribute dfn-for=GPUObjectBase>
: <dfn>\[[device]]</dfn>, of type [=device=], readonly
::
An internal slot holding the [=device=] which owns the [=internal object=].
</dl>
### Object Descriptors ### {#object-descriptors}
An <dfn dfn>object descriptor</dfn> holds the information needed to create an object,
which is typically done via one of the `create*` methods of {{GPUDevice}}.
<script type=idl>
dictionary GPUObjectDescriptorBase {
USVString label;
};
</script>
{{GPUObjectDescriptorBase}} has the following members:
<dl dfn-type=dict-member dfn-for=GPUObjectDescriptorBase>
: <dfn>label</dfn>
::
The initial value of {{GPUObjectBase/label|GPUObjectBase.label}}.
</dl>
## Invalid Internal Objects & Contagious Invalidity ## {#invalidity}
If an object is successfully created, it is <dfn dfn>valid</dfn> at that moment.
An [=internal object=] may be <dfn dfn>invalid</dfn>.
It may become [=invalid=] during its lifetime, but it will never become valid again.
Issue: Consider separating "invalid" from "destroyed".
This would let validity be immutable, and only operations involving devices, buffers, and textures
(e.g. submit, map) would check those objects' `[[destroyed]]` state (explicitly).
<div class=note>
[=Invalid=] objects result from a number of situations, including:
- If there is an error in the creation of an object, it is immediately invalid.
This can happen, for example, if the [=object descriptor=] doesn't describe a valid
object, or if there is not enough memory to allocate a resource.
- If an object is explicitly destroyed (e.g. {{GPUBuffer/destroy()|GPUBuffer.destroy()}}),
it becomes invalid.
- If the [=device=] that owns an object is lost, the object becomes invalid.
</div>
<div algorithm>
To determine if a given {{GPUObjectBase}} |object| is <dfn abstract-op>valid to use with</dfn>
a |targetObject|, run the following steps:
1. If any of the following conditions are unsatisfied return `false`:
<div class=validusage>
- |object| is [=valid=]
- If |targetObject| is a {{GPUDevice}} |object|.{{GPUObjectBase/[[device]]}} is |targetObject|.
- Otherwise |object|.{{GPUObjectBase/[[device]]}} is |targetObject|.{{GPUObjectBase/[[device]]}}.
</div>
1. Return `true`.
</div>
## Coordinate Systems ## {#coordinate-systems}
WebGPU's coordinate systems match DirectX and Metal's coordinate systems in a graphics pipeline.
- Y-axis is up in normalized device coordinate (NDC): point(-1.0, -1.0) in NDC is located at the bottom-left corner of NDC.
In addition, x and y in NDC should be between -1.0 and 1.0 inclusive, while z in NDC should be between 0.0 and 1.0 inclusive.
Vertices out of this range in NDC will not introduce any errors, but they will be clipped.
- Y-axis is down in [=framebuffer=] coordinate, viewport coordinate and fragment/pixel coordinate:
origin(0, 0) is located at the top-left corner in these coordinate systems.
- Window/present coordinate matches [=framebuffer=] coordinate.
- UV of origin(0, 0) in texture coordinate represents the first texel (the lowest byte) in texture memory.
## Programming Model ## {#programming-model}
### Timelines ### {#programming-model-timelines}
*This section is non-normative.*
A computer system with a user agent at the front-end and GPU at the back-end
has components working on different timelines in parallel:
: <dfn dfn>Content timeline</dfn>
:: Associated with the execution of the Web script.
It includes calling all methods described by this specification.
<div class=content-timeline>
Steps executed on the content timeline look like this.
</div>
: <dfn dfn>Device timeline</dfn>
:: Associated with the GPU device operations
that are issued by the user agent.
It includes creation of adapters, devices, and GPU resources
and state objects, which are typically synchronous operations from the point
of view of the user agent part that controls the GPU,
but can live in a separate OS process.
<div class=device-timeline>
Steps executed on the device timeline look like this.
</div>
: <dfn dfn>Queue timeline</dfn>
:: Associated with the execution of operations
on the compute units of the GPU. It includes actual draw, copy,
and compute jobs that run on the GPU.
<div class=queue-timeline>
Steps executed on the queue timeline look like this.
</div>
In this specification, asynchronous operations are used when the result value
depends on work that happens on any timeline other than the [=Content timeline=].
They are represented by callbacks and promises in JavaScript.
<div class="example">
{{GPUComputePassEncoder/dispatch(x, y, z)|GPUComputePassEncoder.dispatch()}}:
1. User encodes a `dispatch` command by calling a method of the
{{GPUComputePassEncoder}} which happens on the [=Content timeline=].
2. User issues {{GPUQueue/submit(commandBuffers)|GPUQueue.submit()}} that hands over
the {{GPUCommandBuffer}} to the user agent, which processes it
on the [=Device timeline=] by calling the OS driver to do a low-level submission.
3. The submit gets dispatched by the GPU invocation scheduler onto the
actual compute units for execution, which happens on the [=Queue timeline=].
</div>
<div class="example">
{{GPUDevice/createBuffer(descriptor)|GPUDevice.createBuffer()}}:
1. User fills out a {{GPUBufferDescriptor}} and creates a {{GPUBuffer}} with it,
which happens on the [=Content timeline=].
2. User agent creates a low-level buffer on the [=Device timeline=].
</div>
<div class="example">
{{GPUBuffer/mapAsync()|GPUBuffer.mapAsync()}}:
1. User requests to map a {{GPUBuffer}} on the [=Content timeline=] and
gets a promise in return.
2. User agent checks if the buffer is currently used by the GPU
and makes a reminder to itself to check back when this usage is over.
3. After the GPU operating on [=Queue timeline=] is done using the buffer,
the user agent maps it to memory and [=resolves=] the promise.
</div>
### Memory Model ### {#programming-model-memory}
*This section is non-normative.*
Once a {{GPUDevice}} has been obtained during an application initialization routine,
we can describe the <dfn dfn>WebGPU platform</dfn> as consisting of the following layers:
1. User agent implementing the specification.
2. Operating system with low-level native API drivers for this device.
3. Actual CPU and GPU hardware.
Each layer of the [=WebGPU platform=] may have different memory types
that the user agent needs to consider when implementing the specification:
- The script-owned memory, such as an {{ArrayBuffer}} created by the script,
is generally not accessible by a GPU driver.
- A user agent may have different processes responsible for running
the content and communication to the GPU driver.
In this case, it uses inter-process shared memory to transfer data.
- Dedicated GPUs have their own memory with high bandwidth,
while integrated GPUs typically share memory with the system.
Most [=physical resources=] are allocated in the memory of type
that is efficient for computation or rendering by the GPU.
When the user needs to provide new data to the GPU,
the data may first need to cross the process boundary in order to reach
the user agent part that communicates with the GPU driver.
Then it may need to be made visible to the driver,
which sometimes requires a copy into driver-allocated staging memory.
Finally, it may need to be transferred to the dedicated GPU memory,
potentially changing the internal layout into one
that is most efficient for GPUs to operate on.
All of these transitions are done by the WebGPU implementation of the user agent.
Note: This example describes the worst case, while in practice
the implementation may not need to cross the process boundary,
or may be able to expose the driver-managed memory directly to
the user behind an `ArrayBuffer`, thus avoiding any data copies.
### Multi-Threading ### {#programming-model-multi-threading}
### Resource Usages ### {#programming-model-resource-usages}
A [=physical resource=] can be used on GPU with an <dfn dfn>internal usage</dfn>:
<dl dfn-type=dfn dfn-for="internal usage">
: <dfn>input</dfn>
:: Buffer with input data for draw or dispatch calls. Preserves the contents.
Allowed by buffer {{GPUBufferUsage/INDEX}}, buffer {{GPUBufferUsage/VERTEX}}, or buffer {{GPUBufferUsage/INDIRECT}}.
: <dfn>constant</dfn>
:: Resource bindings that are constant from the shader point of view. Preserves the contents.
Allowed by buffer {{GPUBufferUsage/UNIFORM}} or texture {{GPUTextureUsage/SAMPLED}}.
: <dfn>storage</dfn>
:: Read-write storage resource binding.
Allowed by buffer {{GPUBufferUsage/STORAGE}}.
: <dfn>storage-read</dfn>
:: Read-only storage resource bindings. Preserves the contents.
Allowed by buffer {{GPUBufferUsage/STORAGE}} or texture {{GPUTextureUsage/STORAGE}}.
: <dfn>storage-write</dfn>
:: Write-only storage resource bindings.
Allowed by texture {{GPUTextureUsage/STORAGE}}.
: <dfn>attachment</dfn>
:: Texture used as an output attachment in a render pass.
Allowed by texture {{GPUTextureUsage/RENDER_ATTACHMENT}}.
: <dfn>attachment-read</dfn>
:: Texture used as a read-only attachment in a render pass. Preserves the contents.
Allowed by texture {{GPUTextureUsage/RENDER_ATTACHMENT}}.
</dl>
Textures may consist of separate [=mipmap levels=] and [=array layers=],
which can be used differently at any given time.
Each such <dfn dfn>texture subresource</dfn> is uniquely identified by a
[=texture=], [=mipmap level=], and
(for {{GPUTextureDimension/2d}} textures only) [=array layer=],
and [=aspect=].
We define <dfn dfn>subresource</dfn> to be either a whole buffer, or a [=texture subresource=].
<div algorithm="compatible usage list">
Some [=internal usages=] are compatible with others. A [=subresource=] can be in a state
that combines multiple usages together. We consider a list |U| to be
a <dfn dfn>compatible usage list</dfn> if (and only if) it satisfies any of the following rules:
- Each usage in |U| is [=internal usage/input=], [=internal usage/constant=], [=internal usage/storage-read=], or [=internal usage/attachment-read=].
- Each usage in |U| is [=internal usage/storage=].
- Each usage in |U| is [=internal usage/storage-write=].
- |U| contains exactly one element: [=internal usage/attachment=].
</div>
Enforcing that the usages are only combined into a [=compatible usage list=]
allows the API to limit when data races can occur in working with memory.
That property makes applications written against
WebGPU more likely to run without modification on different platforms.
Generally, when an implementation processes an operation that uses a [=subresource=]
in a different way than its current usage allows, it schedules a transition of the resource
into the new state. In some cases, like within an open {{GPURenderPassEncoder}}, such a
transition is impossible due to the hardware limitations.
We define these places as <dfn dfn>usage scopes</dfn>.
The **main usage rule** is, for any one [=subresource=], its list of [=internal usages=]
within one [=usage scope=] must be a [=compatible usage list=].
For example, binding the same buffer for [=internal usage/storage=] as well as for
[=internal usage/input=] within the same {{GPURenderPassEncoder}} would put the encoder
as well as the owning {{GPUCommandEncoder}} into the error state.
This combination of usages does not make a [=compatible usage list=].
Note: race condition of multiple writable storage buffer/texture usages in a single [=usage scope=] is allowed.
The [=subresources=] of textures included in the views provided to
{{GPURenderPassColorAttachment/view|GPURenderPassColorAttachment.view}} and
{{GPURenderPassColorAttachment/resolveTarget|GPURenderPassColorAttachment.resolveTarget}}
are considered to be used as [=internal usage/attachment=] for the [=usage scope=] of this render pass.
The <dfn dfn>physical size</dfn> of a [=texture subresource=] is the dimension of the
[=texture subresource=] in texels that includes the possible extra paddings
to form complete [=texel blocks=] in the [=subresource=].
- For pixel-based {{GPUTextureFormat|GPUTextureFormats}}, the [=physical size=] is always equal to the size of the [=texture subresource=]
used in the sampling hardwares.
- [=Textures=] in block-based compressed {{GPUTextureFormat|GPUTextureFormats}} always have a [=mipmap level=] 0 whose {{GPUTexture/[[textureSize]]}}
is a multiple of the [=texel block size=], but the lower mipmap levels might not be
multiples of the [=texel block size=] and can have paddings.
<div class="example">
Considering a {{GPUTexture}} in BC format whose {{GPUTexture/[[textureSize]]}} is {60, 60, 1}, when sampling
the {{GPUTexture}} at [=mipmap level=] 2, the sampling hardware uses {15, 15, 1} as the size of the [=texture subresource=],
while its [=physical size=] is {16, 16, 1} as the block-compression algorithm can only operate on 4x4 [=texel blocks=].
</div>
### Synchronization ### {#programming-model-synchronization}
For each [=subresource=] of a [=physical resource=], its set of
[=internal usage=] flags is tracked on the [=Queue timeline=].
Issue: This section will need to be revised to support multiple queues.
On the [=Queue timeline=], there is an ordered sequence of [=usage scopes=].
For the duration of each scope, the set of [=internal usage=] flags of any given
[=subresource=] is constant.
A [=subresource=] may transition to new usages at the boundaries between [=usage scope=]s.
This specification defines the following [=usage scopes=]:
- Outside of a pass (in {{GPUCommandEncoder}}), each (non-state-setting) command is one usage scope
(e.g. {{GPUCommandEncoder/copyBufferToTexture()}}).
- In a compute pass, each dispatch command ({{GPUComputePassEncoder/dispatch()}} or
{{GPUComputePassEncoder/dispatchIndirect()}}) is one usage scope.
A subresource is "used" in the usage scope if it's accessible by the command.
Within a dispatch, every subresource in every currently bound {{GPUBindGroup}}
is "used" in the usage scope.
State-setting compute pass commands, like
{{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)}},
do not contribute directly to a usage scope; they instead change the
state that is checked in dispatch commands.
- One render pass is one usage scope.
A subresource is "used" in the usage scope if it's referenced by any
(state-setting or non-state-setting) command. For example, in
{{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)}},
every subresource in `bindGroup` is "used" in the render pass's usage scope.
Issue: The above should probably talk about [=GPU commands=]. But we don't have a way to
reference specific GPU commands (like dispatch) yet.
<div class=note>
The above rules mean the following example resource usages **are**
included in [=usage scope validation=]:
- In a render pass, subresources used in any
{{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup()}}
call, regardless of whether the currently bound pipeline's
shader or layout actually depends on these bindings,
or the bind group is shadowed by another 'set' call.
- A buffer used in any {{GPURenderEncoderBase/setVertexBuffer()|setVertexBuffer()}}
call, regardless of whether any draw call depends on this buffer,
or this buffer is shadowed by another 'set' call.
- A buffer used in any {{GPURenderEncoderBase/setIndexBuffer()|setIndexBuffer()}}
call, regardless of whether any draw call depends on this buffer,
or this buffer is shadowed by another 'set' call.
- A texture subresource used as a color attachment, resolve attachment, or
depth/stencil attachment in {{GPURenderPassDescriptor}} by
{{GPUCommandEncoder/beginRenderPass()|beginRenderPass()}},
regardless of whether the shader actually depends on these attachments.
- Resources used in bind group entries with visibility 0, or visible only
to the compute stage but used in a render pass (or vice versa).
</div>
During command encoding, every usage of a subresource is recorded in one of the
[=usage scopes=] in the command buffer.
For each [=usage scope=], the implementation performs
<dfn dfn>usage scope validation</dfn> by composing the list of all
[=internal usage=] flags of each [=subresource=] used in the [=usage scope=].
If any of those lists is not a [=compatible usage list=],
{{GPUCommandEncoder/finish()|GPUCommandEncoder.finish()}}
generates a {{GPUValidationError}} in the current error scope.
## Core Internal Objects ## {#core-internal-objects}
### Adapters ### {#adapters}
An <dfn dfn>adapter</dfn> represents an implementation of WebGPU on the system.
Each adapter identifies both an instance of a hardware accelerator (e.g. GPU or CPU) and
an instance of a browser's implementation of WebGPU on top of that accelerator.
If an [=adapter=] becomes unavailable, it becomes [=invalid=].
Once invalid, it never becomes valid again.
Any [=devices=] on the adapter, and [=internal objects=] owned by those devices,
also become invalid.
Note:
An [=adapter=] may be a physical display adapter (GPU), but it could also be
a software renderer.
A returned [=adapter=] could refer to different physical adapters, or to
different browser codepaths or system drivers on the same physical adapters.
Applications can hold onto multiple [=adapters=] at once (via {{GPUAdapter}})
(even if some are [=invalid=]),
and two of these could refer to different instances of the same physical
configuration (e.g. if the GPU was reset or disconnected and reconnected).
An [=adapter=] has the following internal slots:
<dl dfn-type=attribute dfn-for=adapter>
: <dfn>\[[features]]</dfn>, of type [=ordered set=]<{{GPUFeatureName}}>, readonly
::
The [=features=] which can be used to create devices on this adapter.
: <dfn>\[[limits]]</dfn>, of type [=supported limits=], readonly
::
The [=limit/better|best=] limits which can be used to create devices on this adapter.
Each adapter limit must be the same or [=limit/better=] than its default value
in [=supported limits=].
: <dfn>\[[current]]</dfn>, of type boolean
::
Indicates whether the adapter is allowed to vend new devices at this time.
Its value may change at any time.
It is initially set to `true` inside {{GPU/requestAdapter()}}.
It becomes `false` inside "[=lose the device=]" and "[=mark adapters stale=]".
Once set to `false`, it cannot become `true` again.
Note:
This mechanism ensures that various adapter-creation scenarios look similar to applications,
so they can easily be robust to more scenarios with less testing: first initialization,
reinitialization due to an unplugged adapter, reinitialization due to a test
{{GPUDevice/destroy()|GPUDevice.destroy()}} call, etc. It also ensures applications use
the latest system state to make decisions about which adapter to use.
</dl>
[=Adapters=] are exposed via {{GPUAdapter}}.
### Devices ### {#devices}
A <dfn dfn>device</dfn> is the logical instantiation of an [=adapter=],
through which [=internal objects=] are created.
It can be shared across multiple [=agents=] (e.g. dedicated workers).
A [=device=] is the exclusive owner of all [=internal objects=] created from it:
when the [=device=] is [=lose the device|lost=], it and all objects created on it (directly, e.g.
{{GPUDevice/createTexture()}}, or indirectly, e.g. {{GPUTexture/createView()}}) become
[=invalid=].
Issue: Define "ownership".
A [=device=] has the following internal slots:
<dl dfn-type=attribute dfn-for=device>
: <dfn>\[[adapter]]</dfn>, of type [=adapter=], readonly
::
The [=adapter=] from which this device was created.
: <dfn>\[[features]]</dfn>, of type [=ordered set=]<{{GPUFeatureName}}>, readonly
::
The [=features=] which can be used on this device.
No additional features can be used, even if the underlying [=adapter=] can support them.
: <dfn>\[[limits]]</dfn>, of type [=supported limits=], readonly
::
The limits which can be used on this device.
No [=limit/better=] limits can be used, even if the underlying [=adapter=] can support them.
</dl>
Issue: [=device=] is already an internal concept. Having slots be marked as internal is redundant
and adds extra `[[`/`]]` in spec text. Consider removing the brackets.
<div algorithm>
When <dfn dfn>a new device</dfn> |device| is created from [=adapter=] |adapter|
with {{GPUDeviceDescriptor}} |descriptor|:
- Set |device|.{{device/[[adapter]]}} to |adapter|.
- Set |device|.{{device/[[features]]}} to the [=ordered set|set=] of values in
|descriptor|.{{GPUDeviceDescriptor/nonGuaranteedFeatures}}.
- Let |device|.{{device/[[limits]]}} be a [=supported limits=] object with the default values.
For each (|key|, |value|) pair in |descriptor|.{{GPUDeviceDescriptor/nonGuaranteedLimits}}, set the
member corresponding to |key| in |device|.{{device/[[limits]]}} to the value |value|.
</div>
Any time the user agent needs to revoke access to a device, it calls
[=lose the device=](device, `undefined`).
<div algorithm>
To <dfn dfn>lose the device</dfn>(|device|, |reason|):
1. Set |device|.{{device/[[adapter]]}}.{{adapter/[[current]]}} to `false`.
1. Issue: explain how to get from |device| to its "primary" {{GPUDevice}}.
1. Resolve {{GPUDevice/lost|GPUDevice.lost}} with a new {{GPUDeviceLostInfo}} with
{{GPUDeviceLostInfo/reason}} set to |reason| and
{{GPUDeviceLostInfo/message}} set to an implementation-defined value.
Note: {{GPUDeviceLostInfo/message}} should not disclose unnecessary user/system
information and should never be parsed by applications.
</div>
[=Devices=] are exposed via {{GPUDevice}}.
## Optional Capabilities ## {#optional-capabilities}
WebGPU [=adapters=] and [=devices=] have <dfn dfn>capabilities</dfn>, which
describe WebGPU functionality that differs between different implementations,
typically due to hardware or system software constraints.
A [=capability=] is either a [=feature=] or a [=limit=].
### Features ### {#features}
A <dfn dfn>feature</dfn> is a set of optional WebGPU functionality that is not supported
on all implementations, typically due to hardware or system software constraints.
Each {{GPUAdapter}} exposes a set of available features.
Only those features may be requested in {{GPUAdapter/requestDevice()}}.
Functionality that is part of an feature may only be used if the feature
was requested at device creation. See the [[#feature-index|Feature Index]]
for a description of the functionality each feature enables.
### Limits ### {#limits}
Each <dfn dfn>limit</dfn> is a numeric limit on the usage of WebGPU on a device.
A <dfn dfn>supported limits</dfn> object has a value for every defined limit.
Each [=adapter=] has a set of [=supported limits=], and
[=devices=] are {{GPUDeviceDescriptor/nonGuaranteedLimits|created}} with specific [=supported limits=] in place.
The device limits are enforced regardless of the adapter's limits.
One limit value may be <dfn dfn for=limit>better</dfn> than another.