Skip to content
Permalink
main
Switch branches/tags
Go to file
Addresses a very old (and unimportant) TODO comment.
This PR also clean up a few other TODOs.

Texture dimensions are expressed as GPUIntegerCoordinate, aka "unsigned
long" aka "uint32_t". Therefore, the theoretical maximum mip level is
32 (i.e. the maximum mip level count is 33). For a texture of size 2**32:

- mip level 0: 2**32
- mip level 1: 2**31
- mip level 31: 2**1
- mip level 32: 2**0 = 1

Therefore an lodMaxClamp of 32 is indistinguishable from any larger value,
even at the theoretical limit.
46 contributors

Users who have contributed to this file

@kainino0x @kvark @Kangz @toji @beaufortfrancois @Richard-Yunchao @austinEng @JusSn @Jiawei-Shao @jdashg @haoxli @romandev
<pre class='metadata'>
Title: WebGPU
Shortname: webgpu
Level: None
Status: w3c/ED
Group: webgpu
ED: https://gpuweb.github.io/gpuweb/
TR: https://www.w3.org/TR/webgpu/
Repository: gpuweb/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, w3cid 96977
Editor: Kai Ninomiya, Google https://www.google.com, kainino@google.com, w3cid 99487
Former Editor: Justin Fan, Apple https://www.apple.com, justin_fan@apple.com, w3cid 115633
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=biblio>
{
"WGSL": {
"authors": [
"David Neto",
"Myles C. Maxfield"
],
"href": "https://gpuweb.github.io/gpuweb/wgsl/",
"title": "WebGPU Shading Language",
"status": "Editor's Draft",
"publisher": "W3C",
"deliveredBy": [
"https://github.com/gpuweb/gpuweb"
]
},
"SourceMap": {
"authors": [
"John Lenz",
"Nick Fitzgerald"
],
"href": "https://sourcemaps.info/spec.html",
"title": "Source Map Revision 3 Proposal"
}
}
</pre>
<pre class="link-defaults">
spec:html;
type:interface; text:Navigator
spec:webidl;
type:interface; text:Promise
</pre>
<pre class='anchors'>
spec: ECMA-262; urlPrefix: https://tc39.es/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 capability; url: concept-settings-object-cross-origin-isolated-capability
spec: canvas; urlPrefix: https://html.spec.whatwg.org/multipage/canvas.html#
type: dfn
text: origin-clean; url: concept-canvas-origin-clean
text: placeholder canvas element; url: offscreencanvas-placeholder
text: canvas context mode; url: concept-canvas-context-mode
text: OffscreenCanvas context mode; url: offscreencanvas-context-mode
text: check the usability of the image argument; url: check-the-usability-of-the-image-argument
spec: WGSL; urlPrefix: https://gpuweb.github.io/gpuweb/wgsl/#
type: dfn
text: location; url: input-output-locations
text: interpolation; url: interpolation
text: pipeline-overridable; url: pipeline-overridable
text: pipeline-overridable constant identifier string; url: pipeline-overridable-constant-identifier-string
text: pipeline-overridable constant has a default value; url: pipeline-overridable-constant-has-a-default-value
text: statically accessed; url: statically-accessed
text: wgsl-declaration; url: declaration
text: wgsl-type; url: type
text: pipeline output; url: pipeline-output
text: pipeline input; url: pipeline-input
text: builtin; url: builtin-variables
text: channel formats; url: channel-formats
text: invalid memory reference; url: invalid-memory-reference
</pre>
<style>
/* Make <dl> blocks more distinct from their surroundings. */
main dl:not(.switch) {
border-left: thin solid #f3e48c;
padding-left: .5em;
}
/** In switches, make multiple consecutive <dd> entries distinguishable. */
dl.switch > dd {
display: list-item;
}
/* <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.2-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.
### User Agent State ### {#security-user-agent-state}
This specification doesn't define any additional user-agent state for an origin.
However it is expected that user agents will have compilation caches for the result of expensive
compilation like {{GPUShaderModule}}, {{GPURenderPipeline}} and {{GPUComputePipeline}}.
These caches are important to improve the loading time of WebGPU applications after the first
visit.
For the specification, these caches are indifferentiable from incredibly fast compilation, but
for applications it would be easy to measure how long {{GPUDevice/createComputePipelineAsync()}}
takes to resolve. This can leak information across origins (like "did the user access a site with
this specific shader") so user agents should follow the best practices in
[storage partitioning](https://github.com/privacycg/storage-partitioning).
The system's GPU driver may also have its own cache of compiled shaders and pipelines. User agents
may want to disable these when at all possible, or add per-partition data to shaders in ways that
will make the GPU driver consider them different.
# 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`."
The `?.` ("optional chaining") syntax, adopted from JavaScript, is also used.
The phrasing "`Foo?.Bar`" means
"if `Foo` is `null` or `undefined`, `Foo`; otherwise, `Foo.Bar`".
For example, where `buffer` is a {{GPUBuffer}}, `buffer?.[[device]].[[adapter]]` means
"if `buffer` is `null` or `undefined`, then `undefined`, otherwise,
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 &amp; 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=]
- |object|.{{GPUObjectBase/[[device]]}} is [=valid=].
- |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/TEXTURE_BINDING}}.
: <dfn>storage</dfn>
:: Writable storage resource binding.
Allowed by buffer {{GPUBufferUsage/STORAGE}} or texture {{GPUTextureUsage/STORAGE_BINDING}}.
: <dfn>storage-read</dfn>
:: Read-only storage resource bindings. Preserves the contents.
Allowed by buffer {{GPUBufferUsage/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=].
- |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/[[descriptor]]}}.{{GPUTextureDescriptor/size}}
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/[[descriptor]]}}.{{GPUTextureDescriptor/size}} 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 is potentially accessible by the command.
Within a dispatch, for each bind group slot that is used by the current {{GPUComputePipeline}}'s
{{GPUPipelineBase/[[layout]]}}, every [=subresource=] referenced by
that bind group 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=]&lt;{{GPUFeatureName}}&gt;, 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.
: <dfn>\[[software]]</dfn>, of type boolean
::
If set to `true` indicates that the adapter is backed by a software implementation of an
appropriate graphics API rather than physical GPU hardware.
</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=]&lt;{{GPUFeatureName}}&gt;, 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>
<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/requiredFeatures}}.
- Let |device|.{{device/[[limits]]}} be a [=supported limits=] object with the default values.
For each (|key|, |value|) pair in |descriptor|.{{GPUDeviceDescriptor/requiredLimits}}, set the
member corresponding to |key| in |device|.{{device/[[limits]]}} to the [=limit/better=]
value of |value| or the default value in [=supported limits=].
</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/requiredLimits|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.
A [=limit/better=] limit value always relaxes validation, enabling strictly
more programs to be valid. For each limit, "better" is defined.
Note:
Setting "better" limits may not necessarily be desirable, as they may have a performance impact.
Because of this, and to improve portability across devices and implementations,
applications should generally request the "worst" limits that work for their content
(ideally, the default values).
Each limit also has a <dfn dfn for=limit>default</dfn> value.
Every [=adapter=] is guaranteed to support the default value or [=limit/better=].
The default is used if a value is not explicitly specified in {{GPUDeviceDescriptor/requiredLimits}}.
<table class="data no-colspan-center" dfn-type=attribute dfn-for="supported limits">
<thead>
<tr><th>Limit name <th>Type <th>[=limit/Better=] <th>[=limit/Default=]
</thead>
<tr><td><dfn>maxTextureDimension1D</dfn>
<td>{{GPUSize32}} <td>Higher <td>8192
<tr class=row-continuation><td colspan=4>
The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=Extent3D/width=]
of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"1d"}}.
<tr><td><dfn>maxTextureDimension2D</dfn>
<td>{{GPUSize32}} <td>Higher <td>8192
<tr class=row-continuation><td colspan=4>
The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=Extent3D/width=] and {{GPUTextureDescriptor/size}}.[=Extent3D/height=]
of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"2d"}}.
<tr><td><dfn>maxTextureDimension3D</dfn>
<td>{{GPUSize32}} <td>Higher <td>2048
<tr class=row-continuation><td colspan=4>
The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=Extent3D/width=], {{GPUTextureDescriptor/size}}.[=Extent3D/height=] and {{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=]
of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"3d"}}.
<tr><td><dfn>maxTextureArrayLayers</dfn>
<td>{{GPUSize32}} <td>Higher <td>2048
<tr class=row-continuation><td colspan=4>
The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=]
of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"1d"}} or {{GPUTextureDimension/"2d"}}.
<tr><td><dfn>maxBindGroups</dfn>
<td>{{GPUSize32}} <td>Higher <td>4
<tr class=row-continuation><td colspan=4>
The maximum number of {{GPUBindGroupLayout|GPUBindGroupLayouts}}
allowed in {{GPUPipelineLayoutDescriptor/bindGroupLayouts}}
when creating a {{GPUPipelineLayout}}.
<tr><td><dfn>maxDynamicUniformBuffersPerPipelineLayout</dfn>
<td>{{GPUSize32}} <td>Higher <td>8
<tr class=row-continuation><td colspan=4>
The maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
which are uniform buffers with dynamic offsets.
See [=Exceeds the binding slot limits=].
<tr><td><dfn>maxDynamicStorageBuffersPerPipelineLayout</dfn>
<td>{{GPUSize32}} <td>Higher <td>4
<tr class=row-continuation><td colspan=4>
The maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
which are storage buffers with dynamic offsets.
See [=Exceeds the binding slot limits=].
<tr><td><dfn>maxSampledTexturesPerShaderStage</dfn>
<td>{{GPUSize32}} <td>Higher <td>16
<tr class=row-continuation><td colspan=4>
For each possible {{GPUShaderStage}} `stage`,
the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
which are sampled textures.
See [=Exceeds the binding slot limits=].
<tr><td><dfn>maxSamplersPerShaderStage</dfn>
<td>{{GPUSize32}} <td>Higher <td>16
<tr class=row-continuation><td colspan=4>
For each possible {{GPUShaderStage}} `stage`,
the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
which are samplers.
See [=Exceeds the binding slot limits=].
<tr><td><dfn>maxStorageBuffersPerShaderStage</dfn>
<td>{{GPUSize32}} <td>Higher <td>8
<tr class=row-continuation><td colspan=4>
For each possible {{GPUShaderStage}} `stage`,
the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
which are storage buffers.
See [=Exceeds the binding slot limits=].
<tr><td><dfn>maxStorageTexturesPerShaderStage</dfn>
<td>{{GPUSize32}} <td>Higher <td>4
<tr class=row-continuation><td colspan=4>
For each possible {{GPUShaderStage}} `stage`,
the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
which are storage textures.
See [=Exceeds the binding slot limits=].
<tr><td><dfn>maxUniformBuffersPerShaderStage</dfn>
<td>{{GPUSize32}} <td>Higher <td>12
<tr class=row-continuation><td colspan=4>
For each possible {{GPUShaderStage}} `stage`,
the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
which are uniform buffers.
See [=Exceeds the binding slot limits=].
<tr><td><dfn>maxUniformBufferBindingSize</dfn>
<td>{{GPUSize64}} <td>Higher <td>16384
<tr class=row-continuation><td colspan=4>
The maximum {{GPUBufferBinding}}.{{GPUBufferBinding/size}} for bindings with a
{{GPUBindGroupLayoutEntry}} |entry| for which
|entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"uniform"}}.
<tr><td><dfn>maxStorageBufferBindingSize</dfn>
<td>{{GPUSize64}} <td>Higher <td> 134217728 (128 MiB)
<tr class=row-continuation><td colspan=4>
The maximum {{GPUBufferBinding}}.{{GPUBufferBinding/size}} for bindings with a
{{GPUBindGroupLayoutEntry}} |entry| for which
|entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"storage"}}
or {{GPUBufferBindingType/"read-only-storage"}}.
<tr><td><dfn>minUniformBufferOffsetAlignment</dfn>
<td>{{GPUSize32}} <td>Lower <td>256
<tr class=row-continuation><td colspan=4>
The required alignment for {{GPUBufferBinding}}.{{GPUBufferBinding/offset}} and
{{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup}}
|dynamicOffsets| arguments for binding with a {{GPUBindGroupLayoutEntry}} |entry| for which
|entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"uniform"}}.
<tr><td><dfn>minStorageBufferOffsetAlignment</dfn>
<td>{{GPUSize32}} <td>Lower <td>256
<tr class=row-continuation><td colspan=4>
The required alignment for {{GPUBufferBinding}}.{{GPUBufferBinding/offset}} and
{{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup}}
|dynamicOffsets| arguments for binding with a {{GPUBindGroupLayoutEntry}} |entry| for which
|entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"storage"}}
or {{GPUBufferBindingType/"read-only-storage"}}.
<tr><td><dfn>maxVertexBuffers</dfn>
<td>{{GPUSize32}} <td>Higher <td>8
<tr class=row-continuation><td colspan=4>
The maximum number of {{GPUVertexState/buffers}}
when creating a {{GPURenderPipeline}}.
<tr><td><dfn>maxVertexAttributes</dfn>
<td>{{GPUSize32}} <td>Higher <td>16
<tr class=row-continuation><td colspan=4>
The maximum number of {{GPUVertexBufferLayout/attributes}}
in total across {{GPUVertexState/buffers}}
when creating a {{GPURenderPipeline}}.
<tr><td><dfn>maxVertexBufferArrayStride</dfn>
<td>{{GPUSize32}} <td>Higher <td>2048
<tr class=row-continuation><td colspan=4>
The maximum allowed {{GPUVertexBufferLayout/arrayStride}}
when creating a {{GPURenderPipeline}}.
<tr><td><dfn>maxInterStageShaderComponents</dfn>
<td>{{GPUSize32}} <td>Higher <td>60
<tr class=row-continuation><td colspan=4>
The maximum allowed number of components of input or output variables
for inter-stage communication (like vertex outputs or fragment inputs).
<tr><td><dfn>maxComputeWorkgroupStorageSize</dfn>
<td>{{GPUSize32}} <td>Higher <td>16352
<tr class=row-continuation><td colspan=4>
The maximum number of bytes used for a compute stage
{{GPUShaderModule}} entry-point.
<tr><td><dfn>maxComputeWorkgroupInvocations</dfn>
<td>{{GPUSize32}} <td>Higher <td>256
<tr class=row-continuation><td colspan=4>
The maximum value of the product of the `workgroup_size` values for a
compute stage {{GPUShaderModule}} entry-point.
<tr><td><dfn>maxComputeWorkgroupSize</dfn>
<td>{{GPUExtent3D}} <td>Per-component Higher <td>[256, 256, 64]
<tr class=row-continuation><td colspan=4>
The maximum values of the `workgroup_size` values for a
compute stage {{GPUShaderModule}} entry-point.
<tr><td><dfn>maxComputePerDimensionDispatchSize</dfn>
<td>{{GPUSize32}} <td>Higher <td>65535
<tr class=row-continuation><td colspan=4>
The maximum value for the arguments of {{GPUComputePassEncoder/dispatch(x, y, z)}}.
</table>
Issue: Do we need to have a max per-pixel render target size?
#### <dfn interface>GPUSupportedLimits</dfn> #### {#gpu-supportedlimits}
{{GPUSupportedLimits}} exposes the [=limits=] supported by an adapter or device.
See {{GPUAdapter/limits|GPUAdapter.limits}} and {{GPUDevice/limits|GPUDevice.limits}}.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUSupportedLimits {
readonly attribute unsigned long maxTextureDimension1D;
readonly attribute unsigned long maxTextureDimension2D;
readonly attribute unsigned long maxTextureDimension3D;
readonly attribute unsigned long maxTextureArrayLayers;
readonly attribute unsigned long maxBindGroups;
readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout;
readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout;
readonly attribute unsigned long maxSampledTexturesPerShaderStage;
readonly attribute unsigned long maxSamplersPerShaderStage;
readonly attribute unsigned long maxStorageBuffersPerShaderStage;
readonly attribute unsigned long maxStorageTexturesPerShaderStage;
readonly attribute unsigned long maxUniformBuffersPerShaderStage;
readonly attribute unsigned long long maxUniformBufferBindingSize;
readonly attribute unsigned long long maxStorageBufferBindingSize;
readonly attribute unsigned long minUniformBufferOffsetAlignment;
readonly attribute unsigned long minStorageBufferOffsetAlignment;
readonly attribute unsigned long maxVertexBuffers;
readonly attribute unsigned long maxVertexAttributes;
readonly attribute unsigned long maxVertexBufferArrayStride;
readonly attribute unsigned long maxInterStageShaderComponents;
readonly attribute unsigned long maxComputeWorkgroupStorageSize;
readonly attribute unsigned long maxComputeWorkgroupInvocations;
readonly attribute GPUExtent3D maxComputeWorkgroupSize;
readonly attribute unsigned long maxComputePerDimensionDispatchSize;
};
</script>
#### <dfn interface>GPUSupportedFeatures</dfn> #### {#gpu-supportedfeatures}
{{GPUSupportedFeatures}} is a [=setlike=] interface. Its [=set entries=] are
the {{GPUFeatureName}} values of the [=features=] supported by an adapter or
device. It must only contain strings from the {{GPUFeatureName}} enum.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUSupportedFeatures {
readonly setlike<DOMString>;
};
</script>
<div class="note">
Note: The type of the {{GPUSupportedFeatures}} [=set entries=] is {{DOMString}} to allow user
agents to gracefully handle valid {{GPUFeatureName}}s which are added in later revisions of the spec
but which the user agent has not been updated to recognize yet. If the [=set entries=] type was
{{GPUFeatureName}} the following code would throw an {{TypeError}} rather than reporting `false`:
<div class="example">
Check for support of an unrecognized feature:
<pre highlight="js">
if (adapter.features.has('unknown-feature')) {
// Use unknown-feature
} else {
console.warn('unknown-feature is not supported by this adapter.');
}
</pre>
</div>
</div>
## Origin Restrictions ## {#origin-restrictions}
WebGPU allows accessing image data stored in images, videos, and canvases.
Restrictions are imposed on the use of cross-domain media, because shaders can be used to
indirectly deduce the contents of textures which have been uploaded to the GPU.
WebGPU disallows uploading an image source if it <l spec=html>[=is not origin-clean=]</l>.
This also implies that the [=origin-clean=] flag for a
canvas rendered using WebGPU will never be set to `false`.
For more information on issuing CORS requests for image and video elements, consult:
- [[html#cors-settings-attributes]]
- [[html#the-img-element]] <{img}>
- [[html#media-elements]] {{HTMLMediaElement}}
## Color Spaces and Encoding ## {#color-spaces}
WebGPU does not provide color management. All values within WebGPU (such as texture elements)
are raw numeric values, not color-managed color values.
WebGPU *does* interface with color-managed outputs (via {{GPUCanvasConfiguration}}) and inputs
(via {{GPUQueue/copyExternalImageToTexture()}} and {{GPUDevice/importExternalTexture()}}).
Color conversion must be performed between the WebGPU numeric values and the external color values.
Each such interface point locally defines an encoding (color space, transfer function, and alpha
premultiplication) in which the WebGPU numeric values are to be interpreted.
<script type=idl>
enum GPUPredefinedColorSpace {
"srgb",
};
</script>
Issue: Possibly replace this with {{PredefinedColorSpace}}, but note that doing so would mean new
WebGPU functionality gets added automatically when items are added to that enum in the upstream spec.
Issue(gpuweb/gpuweb#1715):
Consider a path for uploading srgb-encoded images into linearly-encoded textures.
<dl dfn-type=enum-value dfn-for=GPUPredefinedColorSpace>
: <dfn>"srgb"</dfn>
:: The CSS predefined color space <a value for=color()>srgb</a>.
</dl>
# Initialization # {#initialization}
## Examples ## {#initialization-examples}
Issue:
Need a robust example like the one in ErrorHandling.md, which handles all situations.
Possibly also include a simple example with no handling.
## navigator.gpu ## {#navigator-gpu}
A {{GPU}} object is available in the {{Window}} and {{DedicatedWorkerGlobalScope}} contexts through the {{Navigator}}
and {{WorkerNavigator}} interfaces respectively and is exposed via `navigator.gpu`:
<script type=idl>
interface mixin NavigatorGPU {
[SameObject] readonly attribute GPU gpu;
};
Navigator includes NavigatorGPU;
WorkerNavigator includes NavigatorGPU;
</script>
## GPU ## {#gpu-interface}
<dfn interface>GPU</dfn> is the entry point to WebGPU.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPU {
Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {});
};
</script>
{{GPU}} has the following methods:
<dl dfn-type=method dfn-for=GPU>
: <dfn>requestAdapter(options)</dfn>
::
Requests an [=adapter=] from the user agent.
The user agent chooses whether to return an adapter, and, if so,
chooses according to the provided options.
<div algorithm=GPU.requestAdapter>
**Called on:** {{GPU}} |this|.
**Arguments:**
<pre class=argumentdef for="GPU/requestAdapter(options)">
|options|: Criteria used to select the adapter.
</pre>
**Returns:** {{Promise}}&lt;{{GPUAdapter}}?&gt;
1. Let |promise| be [=a new promise=].
1. Issue the following steps on the [=Device timeline=] of |this|:
<div class=device-timeline>
1. If the user agent chooses to return an adapter, it should:
1. Create an [=adapter=] |adapter| with {{adapter/[[current]]}} set to
`true`, chosen according to the rules in [[#adapter-selection]] and the
criteria in |options|.
1. If |adapter| is backed by a software graphics API implementation set
|adapter|.{{adapter/[[software]]}} to `true`.
1. [=Resolve=] |promise| with a new {{GPUAdapter}} encapsulating |adapter|.
1. Otherwise, |promise| [=resolves=] with `null`.
</div>
1. Return |promise|.
<!-- If we add ways to make invalid adapter requests (aside from those
that violate IDL rules), specify that they reject the promise. -->
</div>
</dl>
{{GPU}} has the following internal slots:
<dl dfn-type=attribute dfn-for=GPU>
: <dfn>\[[previously_returned_adapters]]</dfn>, of type [=ordered set=]&lt;[=adapter=]&gt;
::
The set of [=adapters=] that have been returned via {{GPU/requestAdapter()}}.
It is used, then cleared, in [=mark adapters stale=].
</dl>
Upon any change in the system's state that could affect the result of any {{GPU/requestAdapter()}}
call, the user agent *should* [=mark adapters stale=]. For example:
- A physical adapter is added/removed (via plug, driver update, TDR, etc.)
- The system's power configuration has changed (laptop unplugged, power settings changed, etc.)
Additionally, [=mark adapters stale=] may by scheduled at any time.
User agents may choose to do this often even when there has been no system state change (e.g.
several seconds after the last call to {{GPUAdapter/requestDevice()}}.
This has no effect on well-formed applications, obfuscates real system state changes, and makes
developers more aware that calling {{GPU/requestAdapter()}} again is always necessary before
calling {{GPUAdapter/requestDevice()}}.
<div algorithm>
To <dfn dfn>mark adapters stale</dfn>:
1. For each |adapter| in `navigator.gpu.`{{GPU/[[previously_returned_adapters]]}}:
1. Set |adapter|.{{GPUAdapter/[[adapter]]}}.{{adapter/[[current]]}} to `false`.
1. [=list/Empty=] `navigator.gpu.`{{GPU/[[previously_returned_adapters]]}}.
Issue: Update here if an `adaptersadded`/`adapterschanged` event is introduced.
</div>
<div class="example">
Request a {{GPUAdapter}}:
<pre highlight="js">
const adapter = await navigator.gpu.requestAdapter(/* ... */);
const features = adapter.features;
// ...
</pre>
</div>
### Adapter Selection ### {#adapter-selection}
<dfn dictionary>GPURequestAdapterOptions</dfn>
provides hints to the user agent indicating what
configuration is suitable for the application.
<script type=idl>
dictionary GPURequestAdapterOptions {
GPUPowerPreference powerPreference;
boolean forceSoftware = false;
};
</script>
<script type=idl>
enum GPUPowerPreference {
"low-power",
"high-performance"
};
</script>
{{GPURequestAdapterOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=GPURequestAdapterOptions>
: <dfn>powerPreference</dfn>
::
Optionally provides a hint indicating what class of [=adapter=] should be selected from
the system's available adapters.
The value of this hint may influence which adapter is chosen, but it must not
influence whether an adapter is returned or not.
Note:
The primary utility of this hint is to influence which GPU is used in a multi-GPU system.
For instance, some laptops have a low-power integrated GPU and a high-performance
discrete GPU.
Note:
Depending on the exact hardware configuration, such as battery status and attached displays
or removable GPUs, the user agent may select different [=adapters=] given the same power
preference.
Typically, given the same hardware configuration and state and
`powerPreference`, the user agent is likely to select the same adapter.
It must be one of the following values:
<dl dfn-type=enum-value dfn-for=GPUPowerPreference>
: `undefined` (or not present)
::
Provides no hint to the user agent.
: <dfn>"low-power"</dfn>
::
Indicates a request to prioritize power savings over performance.
Note:
Generally, content should use this if it is unlikely to be constrained by drawing
performance; for example, if it renders only one frame per second, draws only relatively
simple geometry with simple shaders, or uses a small HTML canvas element.
Developers are encouraged to use this value if their content allows, since it may
significantly improve battery life on portable devices.
: <dfn>"high-performance"</dfn>
::
Indicates a request to prioritize performance over power consumption.
Note:
By choosing this value, developers should be aware that, for [=devices=] created on the
resulting adapter, user agents are more likely to force device loss, in order to save
power by switching to a lower-power adapter.
Developers are encouraged to only specify this value if they believe it is absolutely
necessary, since it may significantly decrease battery life on portable devices.
</dl>
: <dfn>forceSoftware</dfn>
::
When set to `true` indicates that only software [=adapters=] may be returned. If the user
agent does not support a software [=adapter=], will cause {{GPU/requestAdapter()}} to
resolve to `null`.
Note:
{{GPU/requestAdapter()}} may still return a software [=adapter=] is
{{GPURequestAdapterOptions/forceSoftware}} is set to `false` and either no appropriate
hardware [=adapter=] is available or the user agent chooses not to return a hardware
[=adapter=]. Developers that wish to prevent their applications from running on software
[=adapters=] should check the {{GPUAdapter}}.{{GPUAdapter/isSoftware}} attribute prior to
requesting a {{GPUDevice}}.
</dl>
## <dfn interface>GPUAdapter</dfn> ## {#gpu-adapter}
A {{GPUAdapter}} encapsulates an [=adapter=],
and describes its capabilities ([=features=] and [=limits=]).
To get a {{GPUAdapter}}, use {{GPU/requestAdapter()}}.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUAdapter {
readonly attribute DOMString name;
[SameObject] readonly attribute GPUSupportedFeatures features;
[SameObject] readonly attribute GPUSupportedLimits limits;
readonly attribute boolean isSoftware;
Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
};
</script>
{{GPUAdapter}} has the following attributes:
<dl dfn-type=attribute dfn-for=GPUAdapter>
: <dfn>name</dfn>
::
A human-readable name identifying the adapter.
The contents are implementation-defined.
: <dfn>features</dfn>
::
The set of values in `this`.{{GPUAdapter/[[adapter]]}}.{{adapter/[[features]]}}.
: <dfn>limits</dfn>
::
The limits in `this`.{{GPUAdapter/[[adapter]]}}.{{adapter/[[limits]]}}.
: <dfn>isSoftware</dfn>
::
Returns the value of {{GPUAdapter/[[adapter]]}}.{{adapter/[[software]]}}.
</dl>
{{GPUAdapter}} has the following internal slots:
<dl dfn-type=attribute dfn-for=GPUAdapter>
: <dfn>\[[adapter]]</dfn>, of type [=adapter=], readonly
::
The [=adapter=] to which this {{GPUAdapter}} refers.
</dl>
{{GPUAdapter}} has the following methods:
<dl dfn-type=method dfn-for=GPUAdapter>
: <dfn>requestDevice(descriptor)</dfn>
::
Requests a [=device=] from the [=adapter=].
<div algorithm=GPUAdapter.requestDevice>
**Called on:** {{GPUAdapter}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUAdapter/requestDevice(descriptor)">
|descriptor|: Description of the {{GPUDevice}} to request.
</pre>
**Returns:** {{Promise}}&lt;{{GPUDevice}}?&gt;
1. Let |promise| be [=a new promise=].
1. Let |adapter| be |this|.{{GPUAdapter/[[adapter]]}}.
1. Issue the following steps to the [=Device timeline=]:
<div class=device-timeline>
1. If any of the following requirements are unmet,
[=reject=] |promise| with a {{TypeError}} and stop.
<div class=validusage>
- The set of values in |descriptor|.{{GPUDeviceDescriptor/requiredFeatures}}
must be a subset of those in |adapter|.{{adapter/[[features]]}}.
</div>
1. If any of the following requirements are unmet,
[=reject=] |promise| with an {{OperationError}} and stop.
<div class=validusage>
- Each key in |descriptor|.{{GPUDeviceDescriptor/requiredLimits}}
must be the name of a member of [=supported limits=].
- For each type of limit in [=supported limits=], the value of that
limit in |descriptor|.{{GPUDeviceDescriptor/requiredLimits}}
must be no [=limit/better=] than the value of that limit in
|adapter|.{{adapter/[[limits]]}}.
</div>
1. If |adapter|.{{adapter/[[current]]}} is `false`,
or the user agent otherwise cannot fulfill the request:
1. Let |device| be a new [=device=].
1. [=Lose the device=](|device|, `undefined`).
Note:
This makes |adapter|.{{adapter/[[current]]}} `false`, if it wasn't already.
Note:
User agents should consider issuing developer-visible warnings in
most or all cases when this occurs. Applications should perform
reinitialization logic starting with {{GPU/requestAdapter()}}.
1. [=Resolve=] |promise| with a new {{GPUDevice}} encapsulating |device|,
and stop.
1. [=Resolve=] |promise| with a new {{GPUDevice}} object encapsulating
[=a new device=] with the capabilities described by |descriptor|.
</div>
1. Return |promise|.
</div>
</dl>
### <dfn dictionary>GPUDeviceDescriptor</dfn> ### {#gpudevicedescriptor}
{{GPUDeviceDescriptor}} describes a device request.
<script type=idl>
dictionary GPUDeviceDescriptor : GPUObjectDescriptorBase {
sequence<GPUFeatureName> requiredFeatures = [];
record<DOMString, GPUSize32> requiredLimits = {};
};
</script>
{{GPUDeviceDescriptor}} has the following members:
<dl dfn-type=dict-member dfn-for=GPUDeviceDescriptor>
: <dfn>requiredFeatures</dfn>
::
Specifies the [=features=] that are required by the device request.
The request will fail if the adapter cannot provide these features.
Exactly the specified set of features, and no more or less, will be allowed in validation
of API calls on the resulting device.
: <dfn>requiredLimits</dfn>
::
Specifies the [=limits=] that are required by the device request.
The request will fail if the adapter cannot provide these limits.
Each key must be the name of a member of [=supported limits=].
Exactly the specified limits, and no [=limit/better=] or worse,
will be allowed in validation of API calls on the resulting device.
<!-- If we ever need limit types other than GPUSize32, we can change the value type to
`double` or `any` in the future and write out the type conversion explicitly (by reference
to WebIDL spec). Or change the entire type to `any` and add back a `dictionary GPULimits`
and define the conversion of the whole object by reference to WebIDL. -->
</dl>
#### <dfn enum>GPUFeatureName</dfn> #### {#gpufeaturename}
Each {{GPUFeatureName}} identifies a set of functionality which, if available,
allows additional usages of WebGPU that would have otherwise been invalid.
<script type=idl>
enum GPUFeatureName {
"depth-clamping",
"depth24unorm-stencil8",
"depth32float-stencil8",
"pipeline-statistics-query",
"texture-compression-bc",
"timestamp-query",
};
</script>
## <dfn interface>GPUDevice</dfn> ## {#gpu-device}
A {{GPUDevice}} encapsulates a [=device=] and exposes
the functionality of that device.
{{GPUDevice}} is the top-level interface through which [=WebGPU interfaces=] are created.
To get a {{GPUDevice}}, use {{GPUAdapter/requestDevice()}}.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUDevice : EventTarget {
[SameObject] readonly attribute GPUSupportedFeatures features;
[SameObject] readonly attribute GPUSupportedLimits limits;
[SameObject] readonly attribute GPUQueue queue;
undefined destroy();
GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
GPUTexture createTexture(GPUTextureDescriptor descriptor);
GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
GPUExternalTexture importExternalTexture(GPUExternalTextureDescriptor descriptor);
GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
Promise<GPUComputePipeline> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor);
Promise<GPURenderPipeline> createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor);
GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor);
};
GPUDevice includes GPUObjectBase;
</script>
{{GPUDevice}} has the following attributes:
<dl dfn-type=attribute dfn-for=GPUDevice>
: <dfn>features</dfn>
::
A set containing the {{GPUFeatureName}} values of the features
supported by the device (i.e. the ones with which it was created).
: <dfn>limits</dfn>
::
Exposes the limits supported by the device
(which are exactly the ones with which it was created).
: <dfn>queue</dfn>
::
The primary {{GPUQueue}} for this device.
</dl>
The {{GPUObjectBase/[[device]]}} for a {{GPUDevice}} is the [=device=] that the {{GPUDevice}} refers
to.
{{GPUDevice}} has the methods listed in its WebIDL definition above.
Those not defined here are defined elsewhere in this document.
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>destroy()</dfn>
::
Destroys the [=device=], preventing further operations on it.
Outstanding asynchronous operations will fail.
<div algorithm=GPUDevice.destroy()>
**Called on:** {{GPUDevice}} |this|.
1. [=Lose the device=](|this|.{{GPUObjectBase/[[device]]}},
{{GPUDeviceLostReason/"destroyed"}}).
</div>
Note:
Since no further operations can occur on this device, implementations can free resource
allocations and abort outstanding asynchronous operations immediately.
</dl>
{{GPUDevice}} objects are [=serializable objects=].
Issue(gpuweb/gpuweb#354): Finish defining multithreading API and add `[Serializable]` back to the interface.
<div algorithm>
<dfn abstract-op>The steps to serialize a GPUDevice object</dfn>,
given |value|, |serialized|, and |forStorage|, are:
1. Set |serialized|.agentCluster to be the [=surrounding agent=]'s [=agent cluster=].
1. If |serialized|.agentCluster's [=cross-origin isolated capability=] is false, throw a "{{DataCloneError}}".
1. If |forStorage| is `true`, throw a "{{DataCloneError}}".
1. Set |serialized|.device to the value of |value|.{{GPUObjectBase/[[device]]}}.
</div>
<div algorithm>
<dfn abstract-op>The steps to deserialize a GPUDevice object</dfn>,
given |serialized| and |value|, are:
1. If |serialized|.agentCluster is not the [=surrounding agent=]'s [=agent cluster=], throw a "{{DataCloneError}}".
1. Set |value|.{{GPUObjectBase/[[device]]}} to |serialized|.device.
</div>
Issue: `GPUDevice` doesn't really need the cross-origin policy restriction.
It should be usable from multiple agents regardless. Once we describe the serialization
of buffers, textures, and queues - the COOP+COEP logic should be moved in there.
# Buffers # {#buffers}
## <dfn interface>GPUBuffer</dfn> ## {#buffer-interface}
Issue: define <dfn dfn>buffer</dfn> (internal object)
A {{GPUBuffer}} represents a block of memory that can be used in GPU operations.
Data is stored in linear layout, meaning that each byte of the allocation can be
addressed by its offset from the start of the {{GPUBuffer}}, subject to alignment
restrictions depending on the operation. Some {{GPUBuffer|GPUBuffers}} can be
mapped which makes the block of memory accessible via an {{ArrayBuffer}} called
its mapping.
{{GPUBuffer|GPUBuffers}} are created via
{{GPUDevice/createBuffer(descriptor)|GPUDevice.createBuffer(descriptor)}}
that returns a new buffer in the [=buffer state/mapped=] or [=buffer state/unmapped=] state.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUBuffer {
Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
undefined unmap();
undefined destroy();
};
GPUBuffer includes GPUObjectBase;
</script>
{{GPUBuffer}} has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUBuffer">
: <dfn>\[[size]]</dfn> of type {{GPUSize64}}.
::
The length of the {{GPUBuffer}} allocation in bytes.
: <dfn>\[[usage]]</dfn> of type {{GPUBufferUsageFlags}}.
::
The allowed usages for this {{GPUBuffer}}.
: <dfn>\[[state]]</dfn> of type [=buffer state=].
::
The current state of the {{GPUBuffer}}.
: <dfn>\[[mapping]]</dfn> of type {{ArrayBuffer}} or {{Promise}} or `null`.
::
The mapping for this {{GPUBuffer}}. The {{ArrayBuffer}} isn't directly accessible
and is instead accessed through views into it, called the mapped ranges, that are
stored in {{GPUBuffer/[[mapped_ranges]]}}
Issue(gpuweb/gpuweb#605): Specify {{GPUBuffer/[[mapping]]}} in term of `DataBlock` similarly
to `AllocateArrayBuffer`?
: <dfn>\[[mapping_range]]</dfn> of type [=list=]&lt;{{unsigned long long}}&gt; or `null`.
::
The range of this {{GPUBuffer}} that is mapped.
: <dfn>\[[mapped_ranges]]</dfn> of type [=list=]&lt;{{ArrayBuffer}}&gt; or `null`.
::
The {{ArrayBuffer}}s returned via {{GPUBuffer/getMappedRange}} to the application. They are tracked
so they can be detached when {{GPUBuffer/unmap}} is called.
: <dfn>\[[map_mode]]</dfn> of type {{GPUMapModeFlags}}.
::
The {{GPUMapModeFlags}} of the last call to {{GPUBuffer/mapAsync()}} (if any).
</dl>
Issue: {{GPUBuffer/[[usage]]}} is differently named from {{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/usage}}.
We should make it consistent.
Each {{GPUBuffer}} has a current <dfn dfn>buffer state</dfn> on the [=Content timeline=]
which is one of the following:
- "<dfn dfn for="buffer state">mapped</dfn>" where the {{GPUBuffer}} is
available for CPU operations on its content.
- "<dfn dfn for="buffer state">mapped at creation</dfn>" where the {{GPUBuffer}} was
just created and is available for CPU operations on its content.
- "<dfn dfn for="buffer state">mapping pending</dfn>" where the {{GPUBuffer}} is
being made available for CPU operations on its content.
- "<dfn dfn for="buffer state">unmapped</dfn>" where the {{GPUBuffer}} is
available for GPU operations.
- "<dfn dfn for="buffer state">destroyed</dfn>" where the {{GPUBuffer}} is
no longer available for any operations except {{GPUBuffer/destroy}}.
Note:
{{GPUBuffer/[[size]]}} and {{GPUBuffer/[[usage]]}} are immutable once the
{{GPUBuffer}} has been created.
<div class=note>
Note: {{GPUBuffer}} has a state machine with the following states.
({{GPUBuffer/[[mapping]]}}, {{GPUBuffer/[[mapping_range]]}},
and {{GPUBuffer/[[mapped_ranges]]}} are `null` when not specified.)
- [=buffer state/unmapped=] and [=buffer state/destroyed=].
- [=buffer state/mapped=] or [=buffer state/mapped at creation=] with an
{{ArrayBuffer}} typed {{GPUBuffer/[[mapping]]}}, a sequence of two
numbers in {{GPUBuffer/[[mapping_range]]}} and a sequence of {{ArrayBuffer}}
in {{GPUBuffer/[[mapped_ranges]]}}
- [=buffer state/mapping pending=] with a {{Promise}} typed {{GPUBuffer/[[mapping]]}}.
</div>
{{GPUBuffer}} is {{Serializable}}. It is a reference to an internal buffer
object, and {{Serializable}} means that the reference can be *copied* between
realms (threads/workers), allowing multiple realms to access it concurrently.
Since {{GPUBuffer}} has internal state (mapped, destroyed), that state is
internally-synchronized - these state changes occur atomically across realms.
Issue(gpuweb/gpuweb#354): Finish defining multithreading API and add `[Serializable]` back to the interface.
## Buffer Creation ## {#buffer-creation}
### {{GPUBufferDescriptor}} ### {#GPUBufferDescriptor}
This specifies the options to use in creating a {{GPUBuffer}}.
<script type=idl>
dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
required GPUSize64 size;
required GPUBufferUsageFlags usage;
boolean mappedAtCreation = false;
};
</script>
<div algorithm>
<dfn abstract-op>validating GPUBufferDescriptor</dfn>(device, descriptor)
1. If device is lost return `false`.
1. If any of the bits of |descriptor|'s {{GPUBufferDescriptor/usage}} aren't present in this device's [[allowed buffer usages]] return `false`.
1. If both the {{GPUBufferUsage/MAP_READ}} and {{GPUBufferUsage/MAP_WRITE}} bits of |descriptor|'s {{GPUBufferDescriptor/usage}} attribute are set, return `false`.
1. Return `true`.
</div>
### Buffer Usage ### {#buffer-usage}
<script type=idl>
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
[Exposed=(Window, DedicatedWorker)]
interface GPUBufferUsage {
const GPUFlagsConstant MAP_READ = 0x0001;
const GPUFlagsConstant MAP_WRITE = 0x0002;
const GPUFlagsConstant COPY_SRC = 0x0004;
const GPUFlagsConstant COPY_DST = 0x0008;
const GPUFlagsConstant INDEX = 0x0010;
const GPUFlagsConstant VERTEX = 0x0020;
const GPUFlagsConstant UNIFORM = 0x0040;
const GPUFlagsConstant STORAGE = 0x0080;
const GPUFlagsConstant INDIRECT = 0x0100;
const GPUFlagsConstant QUERY_RESOLVE = 0x0200;
};
</script>
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>createBuffer(descriptor)</dfn>
::
Creates a {{GPUBuffer}}.
<div algorithm=GPUDevice.createBuffer>
**Called on:** {{GPUDevice}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUDevice/createBuffer(descriptor)">
|descriptor|: Description of the {{GPUBuffer}} to create.
</pre>
**Returns:** {{GPUBuffer}}
1. If any of the following conditions are unsatisfied, return an error buffer and stop.
<div class=validusage>
- |this| is a [=valid=] {{GPUDevice}}.
- |descriptor|.{{GPUBufferDescriptor/usage}} is a subset of |this|.[[allowed buffer usages]].
- If |descriptor|.{{GPUBufferDescriptor/usage}} contains {{GPUBufferUsage/MAP_READ}}:
- |descriptor|.{{GPUBufferDescriptor/usage}} contains no other flags
except {{GPUBufferUsage/COPY_DST}}.
- If |descriptor|.{{GPUBufferDescriptor/usage}} contains {{GPUBufferUsage/MAP_WRITE}}:
- |descriptor|.{{GPUBufferDescriptor/usage}} contains no other flags
except {{GPUBufferUsage/COPY_SRC}}.
- If |descriptor|.{{GPUBufferDescriptor/mappedAtCreation}} is `true`:
- |descriptor|.{{GPUBufferDescriptor/size}} is a multiple of 4.
Issue(gpuweb/gpuweb#605): Explain that the resulting error buffer can still be mapped at creation.
Issue(gpuweb/gpuweb#605): Explain what are a {{GPUDevice}}'s `[[allowed buffer usages]]`.
</div>
1. Let |b| be a new {{GPUBuffer}} object.
1. Set |b|.{{GPUBuffer/[[size]]}} to |descriptor|.{{GPUBufferDescriptor/size}}.
1. Set |b|.{{GPUBuffer/[[usage]]}} to |descriptor|.{{GPUBufferDescriptor/usage}}.
1. If |descriptor|.{{GPUBufferDescriptor/mappedAtCreation}} is `true`:
1. Set |b|.{{GPUBuffer/[[mapping]]}} to a new {{ArrayBuffer}} of size |b|.{{GPUBuffer/[[size]]}}.
1. Set |b|.{{GPUBuffer/[[mapping_range]]}} to `[0, descriptor.size]`.
1. Set |b|.{{GPUBuffer/[[mapped_ranges]]}} to `[]`.
1. Set |b|.{{GPUBuffer/[[state]]}} to [=buffer state/mapped at creation=].
Else:
1. Set |b|.{{GPUBuffer/[[mapping]]}} to `null`.
1. Set |b|.{{GPUBuffer/[[mapping_range]]}} to `null`.
1. Set |b|.{{GPUBuffer/[[mapped_ranges]]}} to `null`.
1. Set |b|.{{GPUBuffer/[[state]]}} to [=buffer state/unmapped=].
1. Set each byte of |b|'s allocation to zero.
1. Return |b|.
Note: it is valid to set {{GPUBufferDescriptor/mappedAtCreation}} to `true` without {{GPUBufferUsage/MAP_READ}}
or {{GPUBufferUsage/MAP_WRITE}} in {{GPUBufferDescriptor/usage}}. This can be used to set the buffer's
initial data.
</div>
</dl>
## Buffer Destruction ## {#buffer-destruction}
An application that no longer requires a {{GPUBuffer}} can choose to lose
access to it before garbage collection by calling {{GPUBuffer/destroy()}}.
Note: This allows the user agent to reclaim the GPU memory associated with the {{GPUBuffer}}
once all previously submitted operations using it are complete.
<dl dfn-type=method dfn-for=GPUBuffer>
: <dfn>destroy()</dfn>
::
Destroys the {{GPUBuffer}}.
<div algorithm=GPUBuffer.destroy>
**Called on:** {{GPUBuffer}} |this|.
**Returns:** {{undefined}}
1. If the |this|.{{GPUBuffer/[[state]]}} is [=buffer state/mapped=] or [=buffer state/mapped at creation=]:
1. Run the steps to unmap |this|.
1. Set |this|.{{GPUBuffer/[[state]]}} to [=buffer state/destroyed=].
Issue: Handle error buffers once we have a description of the error monad.
</div>
</dl>
## Buffer Mapping ## {#buffer-mapping}
An application can request to map a {{GPUBuffer}} so that they can access its
content via {{ArrayBuffer}}s that represent part of the {{GPUBuffer}}'s
allocations. Mapping a {{GPUBuffer}} is requested asynchronously with
{{GPUBuffer/mapAsync()}} so that the user agent can ensure the GPU
finished using the {{GPUBuffer}} before the application can access its content.
Once the {{GPUBuffer}} is mapped the application can synchronously ask for access
to ranges of its content with {{GPUBuffer/getMappedRange}}. A mapped {{GPUBuffer}}
cannot be used by the GPU and must be unmapped using {{GPUBuffer/unmap}} before
work using it can be submitted to the [=Queue timeline=].
Issue(gpuweb/gpuweb#605): Add client-side validation that a mapped buffer can
only be unmapped and destroyed on the worker on which it was mapped. Likewise
{{GPUBuffer/getMappedRange}} can only be called on that worker.
<script type=idl>
typedef [EnforceRange] unsigned long GPUMapModeFlags;
[Exposed=(Window, DedicatedWorker)]
interface GPUMapMode {
const GPUFlagsConstant READ = 0x0001;
const GPUFlagsConstant WRITE = 0x0002;
};
</script>
<dl dfn-type=method dfn-for=GPUBuffer>
: <dfn>mapAsync(mode, offset, size)</dfn>
::
Maps the given range of the {{GPUBuffer}} and resolves the returned {{Promise}} when the
{{GPUBuffer}}'s content is ready to be accessed with {{GPUBuffer/getMappedRange()}}.
<div algorithm=GPUBuffer.mapAsync>
**Called on:** {{GPUBuffer}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUBuffer/mapAsync(mode, offset, size)">
|mode|: Whether the buffer should be mapped for reading or writing.
|offset|: Offset in bytes into the buffer to the start of the range to map.
|size|: Size in bytes of the range to map.
</pre>
**Returns:** {{Promise}}&lt;{{undefined}}&gt;
Issue(gpuweb/gpuweb#605): Handle error buffers once we have a description of the error monad.
1. If |size| is missing:
1. Let |rangeSize| be max(0, |this|.{{GPUBuffer/[[size]]}} - |offset|).
Otherwise, let |rangeSize| be |size|.
1. If any of the following conditions are unsatisfied:
<div class=validusage>
- |this| is a [=valid=] {{GPUBuffer}}.
- |offset| is a multiple of 8.
- |rangeSize| is a multiple of 4.
- |offset| + |rangeSize| is less or equal to |this|.{{GPUBuffer/[[size]]}}
- |this|.{{GPUBuffer/[[state]]}} is [=buffer state/unmapped=]
- |mode| contains exactly one of {{GPUMapMode/READ}} or {{GPUMapMode/WRITE}}.
- If |mode| contains {{GPUMapMode/READ}} then |this|.{{GPUBuffer/[[usage]]}} must contain {{GPUBufferUsage/MAP_READ}}.
- If |mode| contains {{GPUMapMode/WRITE}} then |this|.{{GPUBuffer/[[usage]]}} must contain {{GPUBufferUsage/MAP_WRITE}}.
Issue: Do we validate that |mode| contains only valid flags?
</div>
Then:
1. Record a validation error on the current scope.
1. Return [=a promise rejected with=] an {{OperationError}} on the [=Device timeline=].
1. Let |p| be a new {{Promise}}.
1. Set |this|.{{GPUBuffer/[[mapping]]}} to |p|.
1. Set |this|.{{GPUBuffer/[[state]]}} to [=buffer state/mapping pending=].
1. Set |this|.{{GPUBuffer/[[map_mode]]}} to |mode|.
1. Enqueue an operation on the default queue's [=Queue timeline=] that will execute the following:
<div class=queue-timeline>
1. If |this|.{{GPUBuffer/[[state]]}} is [=buffer state/mapping pending=]:
1. Let |m| be a new {{ArrayBuffer}} of size |rangeSize|.
1. Set the content of |m| to the content of |this|'s allocation starting at offset |offset| and for |rangeSize| bytes.
1. Set |this|.{{GPUBuffer/[[mapping]]}} to |m|.
1. Set |this|.{{GPUBuffer/[[state]]}} to [=buffer state/mapped=].
1. Set |this|.{{GPUBuffer/[[mapping_range]]}} to <code>[|offset|, |offset| + |rangeSize|]</code>.
1. Set |this|.{{GPUBuffer/[[mapped_ranges]]}} to `[]`.
1. Resolve |p|.
</div>
1. Return |p|.
</div>
: <dfn>getMappedRange(offset, size)</dfn>
::
Returns a {{ArrayBuffer}} with the contents of the {{GPUBuffer}} in the given mapped range.
<div algorithm=GPUBuffer.getMappedRange>
**Called on:** {{GPUBuffer}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUBuffer/getMappedRange(offset, size)">
|offset|: Offset in bytes into the buffer to return buffer contents from.
|size|: Size in bytes of the {{ArrayBuffer}} to return.
</pre>
**Returns:** {{ArrayBuffer}}
1. If |size| is missing:
1. Let |rangeSize| be max(0, |this|.{{GPUBuffer/[[size]]}} - |offset|).
Otherwise, let |rangeSize| be |size|.
1. If any of the following conditions are unsatisfied, throw an {{OperationError}} and stop.
<div class=validusage>
- |this|.{{GPUBuffer/[[state]]}} is [=buffer state/mapped=] or [=buffer state/mapped at creation=].
- |offset| is a multiple of 8.
- |rangeSize| is a multiple of 4.
- |offset| is greater than or equal to |this|.{{GPUBuffer/[[mapping_range]]}}[0].
- |offset| + |rangeSize| is less than or equal to |this|.{{GPUBuffer/[[mapping_range]]}}[1].
- [|offset|, |offset| + |rangeSize|) does not overlap another range in |this|.{{GPUBuffer/[[mapped_ranges]]}}.
Note: It is always valid to get mapped ranges of a {{GPUBuffer}} that is
[=buffer state/mapped at creation=], even if it is [=invalid=], because
the [=Content timeline=] might not know it is invalid.
Issue: Consider aligning mapAsync offset to 8 to match this.
</div>
1. Let |m| be a new {{ArrayBuffer}} of size |rangeSize| pointing at the content
of |this|.{{GPUBuffer/[[mapping]]}} at offset |offset| - |this|.{{GPUBuffer/[[mapping_range]]}}[0].
1. [=list/Append=] |m| to |this|.{{GPUBuffer/[[mapped_ranges]]}}.
1. Return |m|.
</div>
: <dfn>unmap()</dfn>
::
Unmaps the mapped range of the {{GPUBuffer}} and makes it's contents available for use by the
GPU again.
<div algorithm=GPUBuffer.unmap>
**Called on:** {{GPUBuffer}} |this|.
**Returns:** {{undefined}}
1. If any of the following requirements are unmet, generate a validation
error and stop.
<div class=validusage>
- |this|.{{GPUBuffer/[[state]]}} must be [=buffer state/mapped at creation=],
[=buffer state/mapping pending=], or [=buffer state/mapped=].
Note: It is valid to unmap an [=invalid=] {{GPUBuffer}} that is
[=buffer state/mapped at creation=] because the [=Content timeline=]
might not know it is an error {{GPUBuffer}}.
</div>
1. If |this|.{{GPUBuffer/[[state]]}} is [=buffer state/mapping pending=]:
1. [=Reject=] {{GPUBuffer/[[mapping]]}} with an {{AbortError}}.
1. Set |this|.{{GPUBuffer/[[mapping]]}} to `null`.
1. If |this|.{{GPUBuffer/[[state]]}} is [=buffer state/mapped=] or [=buffer state/mapped at creation=]:
1. If one of the two following conditions holds:
- |this|.{{GPUBuffer/[[state]]}} is [=buffer state/mapped at creation=]
- |this|.{{GPUBuffer/[[state]]}} is [=buffer state/mapped=] and |this|.{{GPUBuffer/[[map_mode]]}} contains {{GPUMapMode/WRITE}}
Then:
1. Enqueue an operation on the default queue's [=Queue timeline=] that updates the |this|.{{GPUBuffer/[[mapping_range]]}}
of |this|'s allocation to the content of |this|.{{GPUBuffer/[[mapping]]}}.
1. Detach each {{ArrayBuffer}} in |this|.{{GPUBuffer/[[mapped_ranges]]}} from its content.
1. Set |this|.{{GPUBuffer/[[mapping]]}} to `null`.
1. Set |this|.{{GPUBuffer/[[mapping_range]]}} to `null`.
1. Set |this|.{{GPUBuffer/[[mapped_ranges]]}} to `null`.
1. Set |this|.{{GPUBuffer/[[state]]}} to [=buffer state/unmapped=].
Note: When a {{GPUBufferUsage/MAP_READ}} buffer (not currently mapped at creation) is
unmapped, any local modifications done by the application to the mapped ranges
{{ArrayBuffer}} are discarded and will not affect the content of follow-up mappings.
</div>
</dl>
# Textures and Texture Views # {#textures}
Issue: define <dfn dfn>texture</dfn> (internal object)
Issue: define <dfn dfn>mipmap level</dfn>, <dfn dfn>array layer</dfn>, <dfn dfn>aspect</dfn>, <dfn dfn>slice</dfn> (concepts)
## <dfn interface>GPUTexture</dfn> ## {#texture-interface}
{{GPUTexture|GPUTextures}} are created via
{{GPUDevice/createTexture(descriptor)|GPUDevice.createTexture(descriptor)}}
that returns a new texture.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUTexture {
GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {});
undefined destroy();
};
GPUTexture includes GPUObjectBase;
</script>
{{GPUTexture}} has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUTexture">
: <dfn>\[[descriptor]]</dfn>, of type {{GPUTextureDescriptor}}
::
The {{GPUTextureDescriptor}} describing this texture.
All optional fields of {{GPUTextureDescriptor}} are defined.
: <dfn>\[[destroyed]]</dfn>, of type `boolean`, initially false
::
If the texture is destroyed, it can no longer be used in any operation,
and its underlying memory can be freed.
</dl>
<div algorithm>
<dfn abstract-op>compute render extent</dfn>(baseSize, mipLevel)
**Arguments:**
- {{GPUExtent3D}} |baseSize|
- {{GPUSize32}} |mipLevel|
**Returns:** {{GPUExtent3DDict}}
1. Let |extent| be a new {{GPUExtent3DDict}} object.
1. Set |extent|.{{GPUExtent3DDict/width}} to max(1, |baseSize|.[=Extent3D/width=] &Gt; |mipLevel|).
1. Set |extent|.{{GPUExtent3DDict/height}} to max(1, |baseSize|.[=Extent3D/height=] &Gt; |mipLevel|).
1. Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to 1.
1. Return |extent|.
</div>
Issue: share this definition with the part of the specification that describes sampling.
### Texture Creation ### {#texture-creation}
<script type=idl>
dictionary GPUTextureDescriptor : GPUObjectDescriptorBase {
required GPUExtent3D size;
GPUIntegerCoordinate mipLevelCount = 1;
GPUSize32 sampleCount = 1;
GPUTextureDimension dimension = "2d";
required GPUTextureFormat format;
required GPUTextureUsageFlags usage;
};
</script>
<script type=idl>
enum GPUTextureDimension {
"1d",
"2d",
"3d",
};
</script>
<script type=idl>
typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
[Exposed=(Window, DedicatedWorker)]
interface GPUTextureUsage {
const GPUFlagsConstant COPY_SRC = 0x01;
const GPUFlagsConstant COPY_DST = 0x02;
const GPUFlagsConstant TEXTURE_BINDING = 0x04;
const GPUFlagsConstant STORAGE_BINDING = 0x08;
const GPUFlagsConstant RENDER_ATTACHMENT = 0x10;
};
</script>
<div algorithm>
<dfn abstract-op>maximum mipLevel count</dfn>(dimension, size)
**Arguments:**
- {{GPUTextureDescriptor/dimension}} |dimension|
- {{GPUTextureDescriptor/size}} |size|
1. Calculate the max dimension value |m|:
- If |dimension| is:
<dl class="switch">
: {{GPUTextureDimension/"1d"}}
:: Let |m| = |size|.[=Extent3D/width=].
: {{GPUTextureDimension/"2d"}}
:: Let |m| = max(|size|.[=Extent3D/width=], |size|.[=Extent3D/height=]).
: {{GPUTextureDimension/"3d"}}
:: Let |m| = max(max(|size|.[=Extent3D/width=], |size|.[=Extent3D/height=]), |size|.[=Extent3D/depthOrArrayLayer=]).
</dl>
1. Return floor(log<sub>2</sub>(|m|)) + 1.
</div>
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>createTexture(descriptor)</dfn>
::
Creates a {{GPUTexture}}.
<div algorithm=GPUDevice.createTexture>
**Called on:** {{GPUDevice}} this.
**Arguments:**
<pre class=argumentdef for="GPUDevice/createTexture(descriptor)">
|descriptor|: Description of the {{GPUTexture}} to create.
</pre>
**Returns:** {{GPUTexture}}
1. Issue the following steps on the [=Device timeline=] of |this|:
<div class=device-timeline>
1. If |descriptor|.{{GPUTextureDescriptor/format}} is a {{GPUTextureFormat}} that requires a feature (see
[[#texture-format-caps]]), but |this|.{{GPUObjectBase/[[device]]}}.{{device/[[features]]}} does not
[=list/contain=] the feature, throw a {{TypeError}}.
1. If any of the following requirements are unmet:
<div class=validusage>
- |this| must be a [=valid=] {{GPUDevice}}.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/width=],
|descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/height=],
and |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=] must be greater than zero.
- |descriptor|.{{GPUTextureDescriptor/mipLevelCount}} must be greater than zero.
- |descriptor|.{{GPUTextureDescriptor/sampleCount}} must be either 1 or 4.
- If |descriptor|.{{GPUTextureDescriptor/dimension}} is:
<dl class="switch">
: {{GPUTextureDimension/"1d"}}
::
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/width=] must be less than or equal to
|this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension1D}}.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/height=] must be 1.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=] must be 1.
- |descriptor|.{{GPUTextureDescriptor/sampleCount}} must be 1.
- |descriptor|.{{GPUTextureDescriptor/format}} must not be a compressed format or depth/stencil format.
: {{GPUTextureDimension/"2d"}}
::
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/width=] must be less than or equal to
|this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension2D}}.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/height=] must be less than or equal to
|this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension2D}}.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=] must be less than
or equal to |this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureArrayLayers}}.
: {{GPUTextureDimension/"3d"}}
::
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/width=] must be less than or equal to
|this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension3D}}.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/height=] must be less than or equal to
|this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension3D}}.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=] must be less than
or equal to |this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension3D}}.
- |descriptor|.{{GPUTextureDescriptor/sampleCount}} must be 1.
- |descriptor|.{{GPUTextureDescriptor/format}} must not be a compressed format or depth/stencil format.
</dl>
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/width=] must be multiple of [=texel block width=].
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/height=] must be multiple of [=texel block height=].
- If |descriptor|.{{GPUTextureDescriptor/sampleCount}} > 1:
- |descriptor|.{{GPUTextureDescriptor/mipLevelCount}} must be 1.
- |descriptor|.{{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=] must be 1.
- |descriptor|.{{GPUTextureDescriptor/usage}} must not include the {{GPUTextureUsage/STORAGE_BINDING}} bit.
- |descriptor|.{{GPUTextureDescriptor/format}} must be a [=renderable format=].
- |descriptor|.{{GPUTextureDescriptor/mipLevelCount}} must be less than or equal to
[$maximum mipLevel count$](|descriptor|.{{GPUTextureDescriptor/dimension}}, |descriptor|.{{GPUTextureDescriptor/size}}).
- |descriptor|.{{GPUTextureDescriptor/usage}} must be a combination of {{GPUTextureUsage}} values.
- If |descriptor|.{{GPUTextureDescriptor/usage}} includes the {{GPUTextureUsage/RENDER_ATTACHMENT}} bit,
|descriptor|.{{GPUTextureDescriptor/format}} must be a [=renderable format=].
- If |descriptor|.{{GPUTextureDescriptor/usage}} includes the {{GPUTextureUsage/STORAGE_BINDING}} bit,
|descriptor|.{{GPUTextureDescriptor/format}} must be listed in [[#plain-color-formats]] table
with {{GPUTextureUsage/STORAGE_BINDING}} capability.
</div>
Then:
1. Generate a {{GPUValidationError}} in the current scope with appropriate error message.
1. Return a new [=invalid=] {{GPUTexture}}.
1. Let |t| be a new {{GPUTexture}} object.
1. Set |t|.{{GPUTexture/[[descriptor]]}} to |descriptor|.
1. Return |t|.
</div>
</div>
</dl>
### Texture Destruction ### {#texture-destruction}
An application that no longer requires a {{GPUTexture}} can choose to lose access to it before
garbage collection by calling {{GPUTexture/destroy()}}.
Note: This allows the user agent to reclaim the GPU memory associated with the {{GPUTexture}} once
all previously submitted operations using it are complete.
<dl dfn-type=method dfn-for=GPUTexture>
: <dfn>destroy()</dfn>
::
Destroys the {{GPUTexture}}.
<div algorithm=GPUTexture.destroy>
**Called on:** {{GPUTexture}} |this|.
**Returns:** {{undefined}}
1. Set |this|.{{GPUTexture/[[destroyed]]}} to true.
</div>
</dl>
## <dfn interface>GPUTextureView</dfn> ## {#gpu-textureview}
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUTextureView {
};
GPUTextureView includes GPUObjectBase;
</script>
{{GPUTextureView}} has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUTextureView">
: <dfn>\[[texture]]</dfn>
::
The {{GPUTexture}} into which this is a view.
: <dfn>\[[descriptor]]</dfn>
::
The {{GPUTextureViewDescriptor}} describing this texture view.
All optional fields of {{GPUTextureViewDescriptor}} are defined.
: <dfn>\[[renderExtent]]</dfn>
::
For renderable views, this is the effective {{GPUExtent3DDict}} for rendering.
Note: this extent depends on the {{GPUTextureViewDescriptor/baseMipLevel}}.
</dl>
### Texture View Creation ### {#texture-view-creation}
<script type=idl>
dictionary GPUTextureViewDescriptor : GPUObjectDescriptorBase {
GPUTextureFormat format;
GPUTextureViewDimension dimension;
GPUTextureAspect aspect = "all";
GPUIntegerCoordinate baseMipLevel = 0;
GPUIntegerCoordinate mipLevelCount;
GPUIntegerCoordinate baseArrayLayer = 0;
GPUIntegerCoordinate arrayLayerCount;
};
</script>
<script type=idl>
enum GPUTextureViewDimension {
"1d",
"2d",
"2d-array",
"cube",
"cube-array",
"3d"
};
</script>
<script type=idl>
enum GPUTextureAspect {
"all",
"stencil-only",
"depth-only"
};
</script>
<dl dfn-type=method dfn-for=GPUTexture>
: <dfn>createView(descriptor)</dfn>
::
Creates a {{GPUTextureView}}.
<div algorithm=GPUTexture.createView>
**Called on:** {{GPUTexture}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUTexture/createView(descriptor)">
|descriptor|: Description of the {{GPUTextureView}} to create.
</pre>
**Returns:** |view|, of type {{GPUTextureView}}.
1. Set |descriptor| to the result of [$resolving GPUTextureViewDescriptor defaults$] with |descriptor|.
1. Issue the following steps on the [=Device timeline=] of |this|:
<div class=device-timeline>
1. If any of the following requirements are unmet:
<div class=validusage>
- |this| is [=valid=]
- If the |descriptor|.{{GPUTextureViewDescriptor/aspect}} is
<dl class="switch">
: {{GPUTextureAspect/"stencil-only"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/format}} must be a [[#depth-formats|depth-stencil format]]
which contains a stencil aspect.
: {{GPUTextureAspect/"depth-only"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/format}} must be a [[#depth-formats|depth-stencil format]]
which contains a depth aspect.
</dl>
- |descriptor|.{{GPUTextureViewDescriptor/mipLevelCount}} must be &gt; 0.
- |descriptor|.{{GPUTextureViewDescriptor/baseMipLevel}} +
|descriptor|.{{GPUTextureViewDescriptor/mipLevelCount}} must be &le;
|this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/mipLevelCount}}.
- |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be &gt; 0.
- |descriptor|.{{GPUTextureViewDescriptor/baseArrayLayer}} +
|descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be &le;
the [$array layer count$] of |this|.
- |descriptor|.{{GPUTextureViewDescriptor/format}} must be |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/format}}.
<div class="issue">Allow for creating views with compatible formats as well.</div>
- If |descriptor|.{{GPUTextureViewDescriptor/dimension}} is:
<dl class="switch">
: {{GPUTextureViewDimension/"1d"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} must be {{GPUTextureDimension/"1d"}}.
:: |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `1`.
: {{GPUTextureViewDimension/"2d"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} must be {{GPUTextureDimension/"2d"}}.
:: |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `1`.
: {{GPUTextureViewDimension/"2d-array"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} must be {{GPUTextureDimension/"2d"}}.
: {{GPUTextureViewDimension/"cube"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} must be {{GPUTextureDimension/"2d"}}.
:: |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `6`.
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/size}}.[=Extent3D/width=] must be
|this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/size}}.[=Extent3D/height=].
: {{GPUTextureViewDimension/"cube-array"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} must be {{GPUTextureDimension/"2d"}}.
:: |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be a multiple of `6`.
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/size}}.[=Extent3D/width=] must be
|this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/size}}.[=Extent3D/height=].
: {{GPUTextureViewDimension/"3d"}}
:: |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} must be {{GPUTextureDimension/"3d"}}.
:: |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `1`.
</dl>
</div>
Then:
1. Generate a {{GPUValidationError}} in the current scope with appropriate error message.
1. Return a new [=invalid=] {{GPUTextureView}}.
1. Let |view| be a new {{GPUTextureView}} object.
1. Set |view|.{{GPUTextureView/[[texture]]}} to |this|.
1. Set |view|.{{GPUTextureView/[[descriptor]]}} to |descriptor|.
1. If |this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/usage}} contains {{GPUTextureUsage/RENDER_ATTACHMENT}}:
1. Let |renderExtent| be [$compute render extent$](|this|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/size}}, |descriptor|.{{GPUTextureViewDescriptor/baseMipLevel}}).
1. Set |view|.{{GPUTextureView/[[renderExtent]]}} to |renderExtent|.
1. Return |view|.
</div>
</div>
</dl>
<div algorithm>
When <dfn abstract-op>resolving GPUTextureViewDescriptor defaults</dfn> for {{GPUTextureViewDescriptor}}
|descriptor| run the following steps:
1. Let |resolved| be a copy of |descriptor|.
1. If |resolved|.{{GPUTextureViewDescriptor/format}} is `undefined`,
set |resolved|.{{GPUTextureViewDescriptor/format}} to |texture|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/format}}.
1. If |resolved|.{{GPUTextureViewDescriptor/mipLevelCount}} is `undefined`,
set |resolved|.{{GPUTextureViewDescriptor/mipLevelCount}} to |texture|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/mipLevelCount}}
&minus; {{GPUTextureViewDescriptor/baseMipLevel}}.
1. If |resolved|.{{GPUTextureViewDescriptor/dimension}} is `undefined` and
|texture|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} is:
<dl class="switch">
: {{GPUTextureDimension/"1d"}}
:: Set |resolved|.{{GPUTextureViewDescriptor/dimension}} to {{GPUTextureViewDimension/"1d"}}.
: {{GPUTextureDimension/"2d"}}
:: Set |resolved|.{{GPUTextureViewDescriptor/dimension}} to {{GPUTextureViewDimension/"2d"}}.
: {{GPUTextureDimension/"3d"}}
:: Set |resolved|.{{GPUTextureViewDescriptor/dimension}} to {{GPUTextureViewDimension/"3d"}}.
</dl>
1. If |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} is `undefined` and
|resolved|.{{GPUTextureViewDescriptor/dimension}} is:
<dl class="switch">
: {{GPUTextureViewDimension/"1d"}}, {{GPUTextureViewDimension/"2d"}}, or
{{GPUTextureViewDimension/"3d"}}
:: Set |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} to `1`.
: {{GPUTextureViewDimension/"cube"}}
:: Set |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} to `6`.
: {{GPUTextureViewDimension/"2d-array"}} or {{GPUTextureViewDimension/"cube-array"}}
:: Set |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} to
|texture|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=] &minus;
{{GPUTextureViewDescriptor/baseArrayLayer}}.
</dl>
1. Return |resolved|.
</div>
<div algorithm>
To determine the <dfn abstract-op>array layer count</dfn> of {{GPUTexture}} |texture|, run the
following steps:
1. If |texture|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/dimension}} is:
<dl class="switch">
: {{GPUTextureDimension/"1d"}} or {{GPUTextureDimension/"3d"}}
:: Return `1`.
: {{GPUTextureDimension/"2d"}}
:: Return |texture|.{{GPUTexture/[[descriptor]]}}.{{GPUTextureDescriptor/size}}.[=Extent3D/depthOrArrayLayers=].
</dl>
</div>
## Texture Formats ## {#texture-formats}
The name of the format specifies the order of components, bits per component,
and data type for the component.
* `r`, `g`, `b`, `a` = red, green, blue, alpha
* `unorm` = unsigned normalized
* `snorm` = signed normalized
* `uint` = unsigned int
* `sint` = signed int
* `float` = floating point
If the format has the `-srgb` suffix, then sRGB conversions from gamma to linear
and vice versa are applied during the reading and writing of color values in the
shader. Compressed texture formats are provided by [=features=]. Their naming
should follow the convention here, with the texture name as a prefix. e.g.
`etc2-rgba8unorm`.
The <dfn dfn>texel block</dfn> is a single addressable element of the textures in pixel-based {{GPUTextureFormat}}s,
and a single compressed block of the textures in block-based compressed {{GPUTextureFormat}}s.
The <dfn dfn>texel block width</dfn> and <dfn dfn>texel block height</dfn> specifies the dimension of one [=texel block=].
- For pixel-based {{GPUTextureFormat}}s, the [=texel block width=] and [=texel block height=] are always 1.
- For block-based compressed {{GPUTextureFormat}}s, the [=texel block width=] is the number of texels in each row of one [=texel block=],
and the [=texel block height=] is the number of texel rows in one [=texel block=].
The <dfn dfn>texel block size</dfn> of a {{GPUTextureFormat}} is the number of bytes to store one [=texel block=].
The [=texel block size=] of each {{GPUTextureFormat}} is constant except for {{GPUTextureFormat/"stencil8"}}, {{GPUTextureFormat/"depth24plus"}}, and {{GPUTextureFormat/"depth24plus-stencil8"}}.
<script type=idl>
enum GPUTextureFormat {
// 8-bit formats
"r8unorm",
"r8snorm",
"r8uint",
"r8sint",
// 16-bit formats
"r16uint",
"r16sint",
"r16float",
"rg8unorm",
"rg8snorm",
"rg8uint",
"rg8sint",
// 32-bit formats
"r32uint",
"r32sint",
"r32float",
"rg16uint",
"rg16sint",
"rg16float",
"rgba8unorm",
"rgba8unorm-srgb",
"rgba8snorm",
"rgba8uint",
"rgba8sint",
"bgra8unorm",
"bgra8unorm-srgb",
// Packed 32-bit formats
"rgb9e5ufloat",
"rgb10a2unorm",
"rg11b10ufloat",
// 64-bit formats
"rg32uint",
"rg32sint",
"rg32float",
"rgba16uint",
"rgba16sint",
"rgba16float",
// 128-bit formats
"rgba32uint",
"rgba32sint",
"rgba32float",
// Depth and stencil formats
"stencil8",
"depth16unorm",
"depth24plus",
"depth24plus-stencil8",
"depth32float",
// BC compressed formats usable if "texture-compression-bc" is both
// supported by the device/user agent and enabled in requestDevice.
"bc1-rgba-unorm",
"bc1-rgba-unorm-srgb",
"bc2-rgba-unorm",
"bc2-rgba-unorm-srgb",
"bc3-rgba-unorm",
"bc3-rgba-unorm-srgb",
"bc4-r-unorm",
"bc4-r-snorm",
"bc5-rg-unorm",
"bc5-rg-snorm",
"bc6h-rgb-ufloat",
"bc6h-rgb-float",
"bc7-rgba-unorm",
"bc7-rgba-unorm-srgb",
// "depth24unorm-stencil8" feature
"depth24unorm-stencil8",
// "depth32float-stencil8" feature
"depth32float-stencil8",
};
</script>
The depth aspect of the {{GPUTextureFormat/"depth24plus"}}) and {{GPUTextureFormat/"depth24plus-stencil8"}})
formats may be implemented as either a 24-bit unsigned normalized value ("depth24unorm")
or a 32-bit IEEE 754 floating point value ("depth32float").
Issue: add something on GPUAdapter(?) that gives an estimate of the bytes per texel of "stencil8"
The {{GPUTextureFormat/stencil8}}) format may be implemented as
either a real "stencil8", or "depth24stencil8", where the depth aspect is
hidden and inaccessible.
Note:
While the precision of depth32float is strictly higher than the precision of
depth24unorm for all values in the representable range (0.0 to 1.0),
note that the set of representable values is not exactly the same:
for depth24unorm, 1 ULP has a constant value of 1 / (2<sup>24</sup> &minus; 1);
for depth32float, 1 ULP has a variable value no greater than 1 / (2<sup>24</sup>).
A <dfn>renderable format</dfn> is either <dfn>color renderable format</dfn>, or <dfn>depth or stencil renderable format</dfn>.
If a format is listed in [[#plain-color-formats]] with {{GPUTextureUsage/RENDER_ATTACHMENT}} capability, it is a
color renderable format. Any other format is not a color renderable format. Any depth/stencil format is a
depth or stencil renderable format. Any other format is not a depth or stencil renderable format.
## <dfn interface>GPUExternalTexture</dfn> ## {#gpu-external-texture}
A {{GPUExternalTexture}} is a sampleable texture wrapping an external video object.
The contents of a {{GPUExternalTexture}} object may not change, either from inside WebGPU
(it is only sampleable) or from outside WebGPU (e.g. due to video frame advancement).
Issue: Update this description with canvas.
They are bound into bind group layouts using the {{GPUBindGroupLayoutEntry/externalTexture}}
bind group layout entry member.
External textures use several binding slots: see [=Exceeds the binding slot limits=].
<div class=note>
External textures *can* be implemented without creating a copy of the imported source,
but this depends implementation-defined factors.
Ownership of the underlying representation may either be exclusive or shared with other
owners (such as a video decoder), but this is not visible to the application.
The underlying representation of an external texture is unobservable
(except for sampling behavior) but typically may include
- Up to three 2D planes of data (e.g. RGBA, Y+UV, Y+U+V).
- Metadata for converting coordinates before reading from those planes (crop and rotation).
- Metadata for converting values into the specified output color space (matrices, gammas, 3D LUT).
The configuration used may not be stable across time, systems, user agents, media sources,
or frames within a single video source.
In order to account for many possible representations,
the binding conservatively uses the following, for *each* external texture:
- three sampled texture bindings (for up to 3 planes),
- one sampled texture binding for a 3D LUT,
- one sampler binding to sample the 3D LUT, and
- one uniform buffer binding for metadata.
</div>
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUExternalTexture {
};
GPUExternalTexture includes GPUObjectBase;
</script>
{{GPUExternalTexture}} has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUExternalTexture">
: <dfn>\[[destroyed]]</dfn>, of type `boolean`
::
Indicates whether the object has been destroyed (can no longer be used).
Initially set to `false`.
<!-- Move this to an internal object if GPUExternalTexture becomes Serializable. -->
</dl>
### Importing External Textures ### {#external-texture-creation}
An external texture is created from an external video object
using {{GPUDevice/importExternalTexture()}}.
Issue: Update this description with canvas.
External textures are destroyed automatically, as a microtask,
instead of manually or upon garbage collection like other resources.
<script type=idl>
dictionary GPUExternalTextureDescriptor : GPUObjectDescriptorBase {
required HTMLVideoElement source;
GPUPredefinedColorSpace colorSpace = "srgb";
};
</script>
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>importExternalTexture(descriptor)</dfn>
::
Creates a {{GPUExternalTexture}} wrapping the provided image source.
<div algorithm=GPUDevice.importExternalTexture>
**Called on:** {{GPUDevice}} this.
**Arguments:**
<pre class=argumentdef for="GPUDevice/importExternalTexture(descriptor)">
|descriptor|: Provides the external image source object (and any creation options).
</pre>
**Returns:** {{GPUExternalTexture}}
1. Let |source| be |descriptor|.{{GPUExternalTextureDescriptor/source}}.
1. Let |usability| be the result of
[=check the usability of the image argument|checking the usability of=] |source|
(which may throw an exception).
1. If |usability| is `bad`, throw an {{InvalidStateError}} and stop.
1. If |source| <l spec=html>[=is not origin-clean=]</l>,
throw a {{SecurityError}} and stop.
1. Let |data| be the result of converting the current image contents of |source| into
the color space |descriptor|.{{GPUExternalTextureDescriptor/colorSpace}}
with unpremultiplied alpha.
Note: This is described like a copy, but may be implemented as a reference to
read-only underlying data plus appropriate metadata to perform conversion later.
Issue: It is currently undetermined whether the default colorSpace, "srgb", is
extended-srgb or clamped-srgb.
This will be determined upstream as the semantics around super-srgb image sources
get defined. Unfortunately we can't sidestep it for now because video sources
can go already go outside the srgb range.
The upstream determination will change whether using the default colorSpace
option can result in sampling values greater than 1.0 or not.
If upstream decides to make "srgb" mean clamped-srgb, we also have the option
of changing our default to `"extended-srgb"`.
1. Let |result| be a new {{GPUExternalTexture}} object wrapping |data|.
1. [=Queue a microtask=] to set |result|.{{GPUExternalTexture/[[destroyed]]}} to `true`,
releasing the underlying resource.
Issue: Is this too restrictive?
1. Return |result|.
</div>
</dl>
### Sampling External Textures ### {#external-texture-sampling}
External textures are represented in WGSL with `texture_external` and may be read using
`textureLoad` and `textureSampleLevel`.
The `sampler` provided to `textureSampleLevel` is used to sample the underlying textures.
The result is in the color space set by {{GPUExternalTextureDescriptor/colorSpace}}.
It is implementation-dependent whether, for any given external texture, the sampler (and filtering)
is applied before or after conversion from underlying values into the specified color space.
Note:
If the internal representation is an RGBA plane, sampling behaves as on a regular 2D texture.
If there are several underlying planes (e.g. Y+UV), the sampler is used to sample each
underlying texture separately, prior to conversion from YUV to the specified color space.
# Samplers # {#samplers}
## <dfn interface>GPUSampler</dfn> ## {#sampler-interface}
A {{GPUSampler}} encodes transformations and filtering information that can
be used in a shader to interpret texture resource data.
{{GPUSampler|GPUSamplers}} are created via {{GPUDevice/createSampler(descriptor)|GPUDevice.createSampler(optional descriptor)}}
that returns a new sampler object.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUSampler {
};
GPUSampler includes GPUObjectBase;
</script>
{{GPUSampler}} has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUSampler">
: <dfn>\[[descriptor]]</dfn>, of type {{GPUSamplerDescriptor}}, readonly
::
The {{GPUSamplerDescriptor}} with which the {{GPUSampler}} was created.
: <dfn>\[[isComparison]]</dfn> of type {{boolean}}.
::
Whether the {{GPUSampler}} is used as a comparison sampler.
: <dfn>\[[isFiltering]]</dfn> of type {{boolean}}.
::
Whether the {{GPUSampler}} weights multiple samples of a texture.
</dl>
## Sampler Creation ## {#sampler-creation}
### {{GPUSamplerDescriptor}} ### {#GPUSamplerDescriptor}
A {{GPUSamplerDescriptor}} specifies the options to use to create a {{GPUSampler}}.
<script type=idl>
dictionary GPUSamplerDescriptor : GPUObjectDescriptorBase {
GPUAddressMode addressModeU = "clamp-to-edge";
GPUAddressMode addressModeV = "clamp-to-edge";
GPUAddressMode addressModeW = "clamp-to-edge";
GPUFilterMode magFilter = "nearest";
GPUFilterMode minFilter = "nearest";
GPUFilterMode mipmapFilter = "nearest";
float lodMinClamp = 0;
float lodMaxClamp = 32;
GPUCompareFunction compare;
[Clamp] unsigned short maxAnisotropy = 1;
};
</script>
- {{GPUSamplerDescriptor/addressModeU}}, {{GPUSamplerDescriptor/addressModeV}},
and {{GPUSamplerDescriptor/addressModeW}} specify the address modes for the texture width,
height, and depth coordinates, respectively.
- {{GPUSamplerDescriptor/magFilter}} specifies the sampling behavior when the sample footprint
is smaller than or equal to one texel.
- {{GPUSamplerDescriptor/minFilter}} specifies the sampling behavior when the sample footprint
is larger than one texel.
- {{GPUSamplerDescriptor/mipmapFilter}} specifies behavior for sampling between two mipmap levels.
- {{GPUSamplerDescriptor/lodMinClamp}} and {{GPUSamplerDescriptor/lodMaxClamp}} specify the minimum and
maximum levels of detail, respectively, used internally when sampling a texture.
- If {{GPUSamplerDescriptor/compare}} is provided, the sampler will be a comparison sampler with the specified
{{GPUCompareFunction}}.
- {{GPUSamplerDescriptor/maxAnisotropy}} specifies the maximum anisotropy value clamp used by the sampler.
Note: most implementations support {{GPUSamplerDescriptor/maxAnisotropy}} values in range between 1 and 16, inclusive.
Issue: explain how LOD is calculated and if there are differences here between platforms.
Issue: explain what anisotropic sampling is
{{GPUAddressMode}} describes the behavior of the sampler if the sample footprint extends beyond
the bounds of the sampled texture.
Issue: Describe a "sample footprint" in greater detail.
<script type=idl>
enum GPUAddressMode {
"clamp-to-edge",
"repeat",
"mirror-repeat"
};
</script>
<dl dfn-type="enum-value" dfn-for=GPUAddressMode>
: <dfn>"clamp-to-edge"</dfn>
::
Texture coordinates are clamped between 0.0 and 1.0, inclusive.
: <dfn>"repeat"</dfn>
::
Texture coordinates wrap to the other side of the texture.
: <dfn>"mirror-repeat"</dfn>
::
Texture coordinates wrap to the other side of the texture, but the texture is flipped
when the integer part of the coordinate is odd.
</dl>
{{GPUFilterMode}} describes the behavior of the sampler if the sample footprint does not exactly
match one texel.
<script type=idl>
enum GPUFilterMode {
"nearest",
"linear"
};
</script>
<dl dfn-type="enum-value" dfn-for=GPUFilterMode>
: <dfn>"nearest"</dfn>
::
Return the value of the texel nearest to the texture coordinates.
: <dfn>"linear"</dfn>
::
Select two texels in each dimension and return a linear interpolation between their values.
</dl>
{{GPUCompareFunction}} specifies the behavior of a comparison sampler. If a comparison sampler is
used in a shader, an input value is compared to the sampled texture value, and the result of this
comparison test (0.0f for pass, or 1.0f for fail) is used in the filtering operation.
Issue: describe how filtering interacts with comparison sampling.
<script type=idl>
enum GPUCompareFunction {
"never",
"less",
"equal",
"less-equal",
"greater",
"not-equal",
"greater-equal",
"always"
};
</script>
<dl dfn-type="enum-value" dfn-for=GPUCompareFunction>
: <dfn>"never"</dfn>
::
Comparison tests never pass.
: <dfn>"less"</dfn>
::
A provided value passes the comparison test if it is less than the sampled value.
: <dfn>"equal"</dfn>
::
A provided value passes the comparison test if it is equal to the sampled value.
: <dfn>"less-equal"</dfn>
::
A provided value passes the comparison test if it is less than or equal to the sampled value.
: <dfn>"greater"</dfn>
::
A provided value passes the comparison test if it is greater than the sampled value.
: <dfn>"not-equal"</dfn>
::
A provided value passes the comparison test if it is not equal to the sampled value.
: <dfn>"greater-equal"</dfn>
::
A provided value passes the comparison test if it is greater than or equal to the sampled value.
: <dfn>"always"</dfn>
::
Comparison tests always pass.
</dl>
<div algorithm>
<dfn abstract-op>validating GPUSamplerDescriptor</dfn>(device, descriptor)
**Arguments:**
- {{GPUDevice}} |device|
- {{GPUSamplerDescriptor}} |descriptor|
**Returns:** {{boolean}}
Return `true` if and only if all of the following conditions are satisfied:
- |device| is valid.
- |descriptor|.{{GPUSamplerDescriptor/lodMinClamp}} is greater than or equal to 0.
- |descriptor|.{{GPUSamplerDescriptor/lodMaxClamp}} is greater than or equal to
|descriptor|.{{GPUSamplerDescriptor/lodMinClamp}}.
- |descriptor|.{{GPUSamplerDescriptor/maxAnisotropy}} is greater than or equal to 1.
- When |descriptor|.{{GPUSamplerDescriptor/maxAnisotropy}} is greater than 1,
|descriptor|.{{GPUSamplerDescriptor/magFilter}}, |descriptor|.{{GPUSamplerDescriptor/minFilter}},
and |descriptor|.{{GPUSamplerDescriptor/mipmapFilter}} must be equal to {{GPUFilterMode/"linear"}}.
</div>
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>createSampler(descriptor)</dfn>
::
Creates a {{GPUBindGroupLayout}}.
<div algorithm=GPUDevice.createSampler>
**Called on:** {{GPUDevice}} this.
**Arguments:**
<pre class=argumentdef for="GPUDevice/createSampler(descriptor)">
|descriptor|: Description of the {{GPUSampler}} to create.
</pre>
**Returns:** {{GPUSampler}}
1. Let |s| be a new {{GPUSampler}} object.
1. Set |s|.{{GPUSampler/[[descriptor]]}} to |descriptor|.
1. Set |s|.{{GPUSampler/[[isComparison]]}} to `false` if the {{GPUSamplerDescriptor/compare}} attribute
of |s|.{{GPUSampler/[[descriptor]]}} is `null` or undefined. Otherwise, set it to `true`.
1. Set |s|.{{GPUSampler/[[isFiltering]]}} to `false` if none of {{GPUSamplerDescriptor/minFilter}},
{{GPUSamplerDescriptor/magFilter}}, or {{GPUSamplerDescriptor/mipmapFilter}} has the value of
{{GPUFilterMode/"linear"}}. Otherwise, set it to `true`.
1. Return |s|.
<div class=validusage dfn-for=GPUDevice.createSampler>
<dfn abstract-op>Valid Usage</dfn>
- If |descriptor| is not `null` or undefined:
- If [$validating GPUSamplerDescriptor$](this, |descriptor|) returns `false`:
1. Generate a {{GPUValidationError}} in the current scope with appropriate error message.
1. Create a new [=invalid=] {{GPUSampler}} and return the result.
</div>
</div>
</dl>
# Resource Binding # {#bindings}
## <dfn interface>GPUBindGroupLayout</dfn> ## {#bind-group-layout}
A {{GPUBindGroupLayout}} defines the interface between a set of resources bound in a {{GPUBindGroup}} and their accessibility in shader stages.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUBindGroupLayout {
};
GPUBindGroupLayout includes GPUObjectBase;
</script>
{{GPUBindGroupLayout}} has the following internal slots:
<dl dfn-type=attribute dfn-for=GPUBindGroupLayout>
: <dfn>\[[descriptor]]</dfn>
::
</dl>
### Creation ### {#bind-group-layout-creation}
A {{GPUBindGroupLayout}} is created via {{GPUDevice/createBindGroupLayout()|GPUDevice.createBindGroupLayout()}}.
<script type=idl>
dictionary GPUBindGroupLayoutDescriptor : GPUObjectDescriptorBase {
required sequence<GPUBindGroupLayoutEntry> entries;
};
</script>
A {{GPUBindGroupLayoutEntry}} describes a single shader resource binding to be included in a {{GPUBindGroupLayout}}.
<script type=idl>
typedef [EnforceRange] unsigned long GPUShaderStageFlags;
[Exposed=(Window, DedicatedWorker)]
interface GPUShaderStage {
const GPUFlagsConstant VERTEX = 0x1;
const GPUFlagsConstant FRAGMENT = 0x2;
const GPUFlagsConstant COMPUTE = 0x4;
};
dictionary GPUBindGroupLayoutEntry {
required GPUIndex32 binding;
required GPUShaderStageFlags visibility;
GPUBufferBindingLayout buffer;
GPUSamplerBindingLayout sampler;
GPUTextureBindingLayout texture;
GPUStorageTextureBindingLayout storageTexture;
GPUExternalTextureBindingLayout externalTexture;
};
</script>
{{GPUBindGroupLayoutEntry}} dictionaries have the following members:
<dl dfn-type=dict-member dfn-for=GPUBindGroupLayoutEntry>
: <dfn>binding</dfn>
::
A unique identifier for a resource binding within a
{{GPUBindGroupLayoutEntry}}, a corresponding {{GPUBindGroupEntry}},
and the {{GPUShaderModule}}s.
: <dfn>visibility</dfn>
::
A bitset of the members of {{GPUShaderStage}}.
Each set bit indicates that a {{GPUBindGroupLayoutEntry}}'s resource
will be accessible from the associated shader stage.
: <dfn>buffer</dfn>
::
When not `undefined`, indicates the [=binding resource type=] for this {{GPUBindGroupLayoutEntry}}
is {{GPUBufferBinding}}.
: <dfn>sampler</dfn>
::
When not `undefined`, indicates the [=binding resource type=] for this {{GPUBindGroupLayoutEntry}}
is {{GPUSampler}}.
: <dfn>texture</dfn>
::
When not `undefined`, indicates the [=binding resource type=] for this {{GPUBindGroupLayoutEntry}}
is {{GPUTextureView}}.
: <dfn>storageTexture</dfn>
::
When not `undefined`, indicates the [=binding resource type=] for this {{GPUBindGroupLayoutEntry}}
is {{GPUTextureView}}.
: <dfn>externalTexture</dfn>
::
When not `undefined`, indicates the [=binding resource type=] for this {{GPUBindGroupLayoutEntry}}
is {{GPUExternalTexture}}.
</dl>
The [=binding member=] of a {{GPUBindGroupLayoutEntry}} is determined by which member of the
{{GPUBindGroupLayoutEntry}} is defined:
{{GPUBindGroupLayoutEntry/buffer}}, {{GPUBindGroupLayoutEntry/sampler}},
{{GPUBindGroupLayoutEntry/texture}}, {{GPUBindGroupLayoutEntry/storageTexture}}, or
{{GPUBindGroupLayoutEntry/externalTexture}}.
Only one may be defined for any given {{GPUBindGroupLayoutEntry}}.
Each member has an associated {{GPUBindingResource}}
type and each [=binding type=] has an associated [=internal usage=], given by this table:
<table class="data" style="white-space: nowrap">
<thead>
<tr>
<th><dfn dfn>Binding member</dfn>
<th><dfn dfn lt="Binding Resource Type">Resource type</dfn>
<th><dfn dfn>Binding type</dfn><br>
<th><dfn dfn>Binding usage</dfn>
</thead>
<tr>
<td rowspan=3>{{GPUBindGroupLayoutEntry/buffer}}
<td rowspan=3>{{GPUBufferBinding}}
<td>{{GPUBufferBindingType/"uniform"}}
<td>[=internal usage/constant=]
<tr>
<td>{{GPUBufferBindingType/"storage"}}
<td>[=internal usage/storage=]
<tr>
<td>{{GPUBufferBindingType/"read-only-storage"}}
<td>[=internal usage/storage-read=]
<tr>
<td rowspan=3>{{GPUBindGroupLayoutEntry/sampler}}
<td rowspan=3>{{GPUSampler}}
<td>{{GPUSamplerBindingType/"filtering"}}
<td rowspan=3>[=internal usage/constant=]
<tr>
<td>{{GPUSamplerBindingType/"non-filtering"}}
<tr>
<td>{{GPUSamplerBindingType/"comparison"}}
<tr>
<td rowspan=5>{{GPUBindGroupLayoutEntry/texture}}
<td rowspan=5>{{GPUTextureView}}
<td>{{GPUTextureSampleType/"float"}}
<td rowspan=5>[=internal usage/constant=]
<tr>
<td>{{GPUTextureSampleType/"unfilterable-float"}}
<tr>
<td>{{GPUTextureSampleType/"depth"}}
<tr>
<td>{{GPUTextureSampleType/"sint"}}
<tr>
<td>{{GPUTextureSampleType/"uint"}}
<tr>
<td>{{GPUBindGroupLayoutEntry/storageTexture}}
<td>{{GPUTextureView}}
<td>{{GPUStorageTextureAccess/"write-only"}}
<td>[=internal usage/storage=]
<tr>
<td>{{GPUBindGroupLayoutEntry/externalTexture}}
<td>{{GPUExternalTexture}}
<td>
<td>[=internal usage/constant=]
</table>
<div algorithm>
The [=list=] of {{GPUBindGroupLayoutEntry}} values |entries|
<dfn>exceeds the binding slot limits</dfn> of [=supported limits=] |limits|
if the number of slots used toward a limit exceeds the supported value in |limits|.
Each entry may use multiple slots toward multiple limits.
1. For each |entry| in |entries|, if:
<dl class=switch>
: |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"uniform"}} and
|entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/hasDynamicOffset}} is `true`
:: Consider 1 {{supported limits/maxDynamicUniformBuffersPerPipelineLayout}} slot to be used.
: |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"storage"}} and
|entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/hasDynamicOffset}} is `true`
:: Consider 1 {{supported limits/maxDynamicStorageBuffersPerPipelineLayout}} slot to be used.
</dl>
1. For each shader stage |stage| in
&laquo; {{GPUShaderStage/VERTEX}}, {{GPUShaderStage/FRAGMENT}}, {{GPUShaderStage/COMPUTE}} &raquo;:
1. For each |entry| in |entries| for which
|entry|.{{GPUBindGroupLayoutEntry/visibility}} contains |stage|, if:
<dl class=switch>
: |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"uniform"}}
:: Consider 1 {{supported limits/maxUniformBuffersPerShaderStage}} slot to be used.
: |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
is {{GPUBufferBindingType/"storage"}} or {{GPUBufferBindingType/"read-only-storage"}}
:: Consider 1 {{supported limits/maxStorageBuffersPerShaderStage}} slot to be used.
: |entry|.{{GPUBindGroupLayoutEntry/sampler}} is not `undefined`
:: Consider 1 {{supported limits/maxSamplersPerShaderStage}} slot to be used.
: |entry|.{{GPUBindGroupLayoutEntry/texture}} is not `undefined`
:: Consider 1 {{supported limits/maxSampledTexturesPerShaderStage}} slot to be used.
: |entry|.{{GPUBindGroupLayoutEntry/storageTexture}} is not `undefined`
:: Consider 1 {{supported limits/maxStorageTexturesPerShaderStage}} slot to be used.
: |entry|.{{GPUBindGroupLayoutEntry/externalTexture}} is not `undefined`
:: Consider
4 {{supported limits/maxSampledTexturesPerShaderStage}} slot,
1 {{supported limits/maxSamplersPerShaderStage}} slot, and
1 {{supported limits/maxUniformBuffersPerShaderStage}} slot
to be used.
</dl>
</div>
<script type=idl>
enum GPUBufferBindingType {
"uniform",
"storage",
"read-only-storage",
};
dictionary GPUBufferBindingLayout {
GPUBufferBindingType type = "uniform";
boolean hasDynamicOffset = false;
GPUSize64 minBindingSize = 0;
};
</script>
{{GPUBufferBindingLayout}} dictionaries have the following members:
<dl dfn-type=dict-member dfn-for=GPUBufferBindingLayout>
: <dfn>type</dfn>
::
Indicates the type required for buffers bound to this bindings.
: <dfn>hasDynamicOffset</dfn>
::
Indicates whether this binding requires a dynamic offset.
: <dfn>minBindingSize</dfn>
::
May be used to indicate the minimum buffer binding size.
</dl>
<script type=idl>
enum GPUSamplerBindingType {
"filtering",
"non-filtering",
"comparison",
};
dictionary GPUSamplerBindingLayout {
GPUSamplerBindingType type = "filtering";
};
</script>
{{GPUSamplerBindingLayout}} dictionaries have the following members:
<dl dfn-type=dict-member dfn-for=GPUSamplerBindingLayout>
: <dfn>type</dfn>
::
Indicates the required type of a sampler bound to this bindings.
</dl>
<script type=idl>
enum GPUTextureSampleType {
"float",
"unfilterable-float",
"depth",
"sint",
"uint",
};
dictionary GPUTextureBindingLayout {
GPUTextureSampleType sampleType = "float";
GPUTextureViewDimension viewDimension = "2d";
boolean multisampled = false;
};
</script>
Issue(https://github.com/gpuweb/gpuweb/issues/851): consider making {{GPUTextureBindingLayout/sampleType}}
truly optional.
{{GPUTextureBindingLayout}} dictionaries have the following members:
<dl dfn-type=dict-member dfn-for=GPUTextureBindingLayout>
: <dfn>sampleType</dfn>
::
Indicates the type required for texture views bound to this binding.
: <dfn>viewDimension</dfn>
::
Indicates the required {{GPUTextureViewDescriptor/dimension}} for texture views bound to
this binding.
Note:
This enables Metal-based WebGPU implementations to back the respective bind groups with
`MTLArgumentBuffer` objects that are more efficient to bind at run-time.
: <dfn>multisampled</dfn>
::
Inicates whether or not texture views bound to this binding must be multisampled.
</dl>
<script type=idl>
enum GPUStorageTextureAccess {
"write-only",
};
dictionary GPUStorageTextureBindingLayout {
GPUStorageTextureAccess access = "write-only";
required GPUTextureFormat format;
GPUTextureViewDimension viewDimension = "2d";
};
</script>
Issue(https://github.com/gpuweb/gpuweb/issues/851): consider making {{GPUStorageTextureBindingLayout/format}}
truly optional.
{{GPUStorageTextureBindingLayout}} dictionaries have the following members:
<dl dfn-type=dict-member dfn-for=GPUStorageTextureBindingLayout>
: <dfn>access</dfn>
::
Indicates whether texture views bound to this binding will be bound for read-only or
write-only access.
: <dfn>format</dfn>
::
The required {{GPUTextureViewDescriptor/format}} of texture views bound to this binding.
: <dfn>viewDimension</dfn>
::
Indicates the required {{GPUTextureViewDescriptor/dimension}} for texture views bound to
this binding.
Note:
This enables Metal-based WebGPU implementations to back the respective bind groups with
`MTLArgumentBuffer` objects that are more efficient to bind at run-time.
</dl>
<script type=idl>
dictionary GPUExternalTextureBindingLayout {
};
</script>
A {{GPUBindGroupLayout}} object has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUBindGroupLayout">
: <dfn>\[[entryMap]]</dfn> of type [=ordered map=]&lt;{{GPUSize32}}, {{GPUBindGroupLayoutEntry}}&gt.
::
The map of binding indices pointing to the {{GPUBindGroupLayoutEntry}}s,
which this {{GPUBindGroupLayout}} describes.
: <dfn>\[[dynamicOffsetCount]]</dfn> of type {{GPUSize32}}.
::
The number of buffer bindings with dynamic offsets in this {{GPUBindGroupLayout}}.
</dl>
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>createBindGroupLayout(descriptor)</dfn>
::
Creates a {{GPUBindGroupLayout}}.
<div algorithm=GPUDevice.createBindGroupLayout>
**Called on:** {{GPUDevice}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUDevice/createBindGroupLayout(descriptor)">
|descriptor|: Description of the {{GPUBindGroupLayout}} to create.
</pre>
**Returns:** {{GPUBindGroupLayout}}
1. Let |layout| be a new valid {{GPUBindGroupLayout}} object.
1. Set |layout|.{{GPUBindGroupLayout/[[descriptor]]}} to |descriptor|.
1. Issue the following steps on the [=Device timeline=] of |this|:
<div class=device-timeline>
1. If any of the following conditions are unsatisfied:
<div class=validusage>
- |this| is a [=valid=] {{GPUDevice}}.
- The {{GPUBindGroupLayoutEntry/binding}} of each entry in |descriptor| is unique.
- |descriptor|.{{GPUBindGroupLayoutDescriptor/entries}} must not
[=exceeds the binding slot limits|exceed the binding slot limits=]
of |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.
- For each {{GPUBindGroupLayoutEntry}} |entry| in |descriptor|.{{GPUBindGroupLayoutDescriptor/entries}}:
- Let |bufferLayout| be |entry|.{{GPUBindGroupLayoutEntry/buffer}}
- Let |samplerLayout| be |entry|.{{GPUBindGroupLayoutEntry/sampler}}
- Let |textureLayout| be |entry|.{{GPUBindGroupLayoutEntry/texture}}
- Let |storageTextureLayout| be |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}
- Exactly one of |bufferLayout|, |samplerLayout|, |textureLayout|,
or |storageTextureLayout| are not `undefined`.
- If |entry|.{{GPUBindGroupLayoutEntry/visibility}} includes
{{GPUShaderStage/VERTEX}}:
- |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
must not be {{GPUBufferBindingType/"storage"}}.
- |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}?.{{GPUStorageTextureBindingLayout/access}}
must not be {{GPUStorageTextureAccess/"write-only"}}.
- If the |textureLayout| is not `undefined` and
|textureLayout|.{{GPUTextureBindingLayout/multisampled}} is `true`:
- |textureLayout|.{{GPUTextureBindingLayout/viewDimension}} is
{{GPUTextureViewDimension/"2d"}}.
- |textureLayout|.{{GPUTextureBindingLayout/sampleType}} is not
{{GPUTextureSampleType/"float"}} or {{GPUTextureSampleType/"depth"}}.
- If |storageTextureLayout| is not `undefined`:
- |storageTextureLayout|.{{GPUStorageTextureBindingLayout/viewDimension}} is not
{{GPUTextureViewDimension/"cube"}} or {{GPUTextureViewDimension/"cube-array"}}.
- |storageTextureLayout|.{{GPUStorageTextureBindingLayout/format}} must be a format
which can support storage usage.
</div>
Then:
1. Generate a {{GPUValidationError}} in the current scope with appropriate
error message.
1. Make |layout| [=invalid=] and return |layout|.
1. Set |layout|.{{GPUBindGroupLayout/[[dynamicOffsetCount]]}} to the number of
entries in |descriptor| where {{GPUBindGroupLayoutEntry/buffer}} is not `undefined` and
{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/hasDynamicOffset}} is `true`.
1. For each {{GPUBindGroupLayoutEntry}} |entry| in
|descriptor|.{{GPUBindGroupLayoutDescriptor/entries}}:
1. Insert |entry| into |layout|.{{GPUBindGroupLayout/[[entryMap]]}}
with the key of |entry|.{{GPUBindGroupLayoutEntry/binding}}.
</div>
1. Return |layout|.
</div>
</dl>
### Compatibility ### {#bind-group-compatibility}
<div algorithm>
Two {{GPUBindGroupLayout}} objects |a| and |b| are considered <dfn dfn>group-equivalent</dfn>
if and only if, for any binding number |binding|, one of the following conditions is satisfied:
- it's missing from both |a|.{{GPUBindGroupLayout/[[entryMap]]}} and |b|.{{GPUBindGroupLayout/[[entryMap]]}}.
- |a|.{{GPUBindGroupLayout/[[entryMap]]}}[|binding|] == |b|.{{GPUBindGroupLayout/[[entryMap]]}}[|binding|]
</div>
If bind groups layouts are [=group-equivalent=] they can be interchangeably used in all contents.
## <dfn interface>GPUBindGroup</dfn> ## {#gpu-bind-group}
A {{GPUBindGroup}} defines a set of resources to be bound together in a group
and how the resources are used in shader stages.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUBindGroup {
};
GPUBindGroup includes GPUObjectBase;
</script>
### Bind Group Creation ### {#bind-group-creation}
A {{GPUBindGroup}} is created via {{GPUDevice/createBindGroup()|GPUDevice.createBindGroup()}}.
<script type=idl>
dictionary GPUBindGroupDescriptor : GPUObjectDescriptorBase {
required GPUBindGroupLayout layout;
required sequence<GPUBindGroupEntry> entries;
};
</script>
A {{GPUBindGroupEntry}} describes a single resource to be bound in a {{GPUBindGroup}}.
<script type=idl>
typedef (GPUSampler or GPUTextureView or GPUBufferBinding or GPUExternalTexture) GPUBindingResource;
dictionary GPUBindGroupEntry {
required GPUIndex32 binding;
required GPUBindingResource resource;
};
</script>
<script type=idl>
dictionary GPUBufferBinding {
required GPUBuffer buffer;
GPUSize64 offset = 0;
GPUSize64 size;
};
</script>
* {{GPUBufferBinding/size}}: If undefined, specifies the range starting at
{{GPUBufferBinding/offset}} and ending at the end of the buffer.
A {{GPUBindGroup}} object has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUBindGroup">
: <dfn>\[[layout]]</dfn> of type {{GPUBindGroupLayout}}.
::
The {{GPUBindGroupLayout}} associated with this {{GPUBindGroup}}.
: <dfn>\[[entries]]</dfn> of type sequence<{{GPUBindGroupEntry}}>.
::
The set of {{GPUBindGroupEntry}}s this {{GPUBindGroup}} describes.
: <dfn>\[[usedResources]]</dfn> of type [=ordered map=]&lt;[=subresource=], [=list=]&lt;[=internal usage=]&gt;&gt;.
::
The set of buffer and texture [=subresource=]s used by this bind group,
associated with lists of the [=internal usage=] flags.
</dl>
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>createBindGroup(descriptor)</dfn>
::
Creates a {{GPUBindGroup}}.
<div algorithm=GPUDevice.createBindGroup>
**Called on:** {{GPUDevice}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUDevice/createBindGroup(descriptor)">
|descriptor|: Description of the {{GPUBindGroup}} to create.
</pre>
**Returns:** {{GPUBindGroup}}
1. Let |bindGroup| be a new valid {{GPUBindGroup}} object.
1. Let |limits| be |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.
1. Issue the following steps on the [=Device timeline=] of |this|:
<div class=device-timeline>
1. If any of the following conditions are unsatisfied:
<div class=validusage>
- |descriptor|.{{GPUBindGroupDescriptor/layout}} is [$valid to use with$] |this|.
- The number of {{GPUBindGroupLayoutDescriptor/entries}} of
|descriptor|.{{GPUBindGroupDescriptor/layout}} is exactly equal to
the number of |descriptor|.{{GPUBindGroupDescriptor/entries}}.
For each {{GPUBindGroupEntry}} |bindingDescriptor| in
|descriptor|.{{GPUBindGroupDescriptor/entries}}:
- Let |resource| be |bindingDescriptor|.{{GPUBindGroupEntry/resource}}.
- There is exactly one {{GPUBindGroupLayoutEntry}} |layoutBinding|
in |descriptor|.{{GPUBindGroupDescriptor/layout}}.{{GPUBindGroupLayoutDescriptor/entries}}
such that |layoutBinding|.{{GPUBindGroupLayoutEntry/binding}} equals to
|bindingDescriptor|.{{GPUBindGroupEntry/binding}}.
- If the defined [=binding member=] for |layoutBinding| is
<dl class="switch">
: {{GPUBindGroupLayoutEntry/sampler}}
::
- |resource| is a {{GPUSampler}}.
- |resource| is [$valid to use with$] |this|.
- If |layoutBinding|.{{GPUBindGroupLayoutEntry/sampler}}.{{GPUSamplerBindingLayout/type}} is:
<dl class="switch">
: {{GPUSamplerBindingType/"filtering"}}
:: |resource|.{{GPUSampler/[[isComparison]]}} is `false`.
: {{GPUSamplerBindingType/"non-filtering"}}
::
|resource|.{{GPUSampler/[[isFiltering]]}} is `false`.
|resource|.{{GPUSampler/[[isComparison]]}} is `false`.
: {{GPUSamplerBindingType/"comparison"}}
:: |resource|.{{GPUSampler/[[isComparison]]}} is `true`.
</dl>
: {{GPUBindGroupLayoutEntry/texture}}
::
- |resource| is a {{GPUTextureView}}.
- |resource| is [$valid to use with$] |this|.
- Let |texture| be |resource|.{{GPUTextureView/[[texture]]}}.
- |layoutBinding|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/viewDimension}}
is equal to |resource|'s {{GPUTextureViewDescriptor/dimension}}.
- |layoutBinding|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}}
is [[#texture-format-caps|compatible]] with
|resource|'s {{GPUTextureViewDescriptor/format}}.
- |texture|'s {{GPUTextureDescriptor/usage}} includes {{GPUTextureUsage/TEXTURE_BINDING}}.
- If |layoutBinding|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/multisampled}}
is `true`, |texture|'s {{GPUTextureDescriptor/sampleCount}}
&gt; `1`, Otherwise |texture|'s {{GPUTextureDescriptor/sampleCount}} is `1`.
: {{GPUBindGroupLayoutEntry/storageTexture}}
::
- |resource| is a {{GPUTextureView}}.
- |resource| is [$valid to use with$] |this|.
- Let |texture| be |resource|.{{GPUTextureView/[[texture]]}}.
- |layoutBinding|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/viewDimension}}
is equal to |resource|'s {{GPUTextureViewDescriptor/dimension}}.
- |layoutBinding|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/format}}
is equal to |resource|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/format}}.
- |texture|'s {{GPUTextureDescriptor/usage}} includes {{GPUTextureUsage/STORAGE_BINDING}}.
: {{GPUBindGroupLayoutEntry/buffer}}
::
- |resource| is a {{GPUBufferBinding}}.
- |resource|.{{GPUBufferBinding/buffer}} is [$valid to use with$] |this|.
- The bound part designated by |resource|.{{GPUBufferBinding/offset}} and
|resource|.{{GPUBufferBinding/size}} resides inside the buffer.
- If |layoutBinding|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}}
is not `undefined`:
- The effective binding size, that is either explict in
|resource|.{{GPUBufferBinding/size}} or derived from
|resource|.{{GPUBufferBinding/offset}} and the full
size of the buffer, is greater than or equal to
|layoutBinding|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}}.
- If |layoutBinding|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/type}} is
<dl class="switch">
: {{GPUBufferBindingType/"uniform"}}
:: |resource|.{{GPUBufferBinding/buffer}}.{{GPUBufferDescriptor/usage}}
includes {{GPUBufferUsage/UNIFORM}}.
:: |resource|.{{GPUBufferBinding/size}} &le;
|limits|.{{supported limits/maxUniformBufferBindingSize}}.
:: |resource|.{{GPUBufferBinding/offset}} is a multiple of
|limits|.{{supported limits/minUniformBufferOffsetAlignment}}.
:: Issue: This validation should take into account the default when {{GPUBufferBinding/size}} is not set.
Also should {{GPUBufferBinding/size}} default to the `buffer.byteLength - offset` or
`min(buffer.byteLength - offset, limits.maxUniformBufferBindingSize)`?
: {{GPUBufferBindingType/"storage"}} or
{{GPUBufferBindingType/"read-only-storage"}}
:: |resource|.{{GPUBufferBinding/buffer}}.{{GPUBufferDescriptor/usage}}
includes {{GPUBufferUsage/STORAGE}}.
:: |resource|.{{GPUBufferBinding/size}} &le;
|limits|.{{supported limits/maxStorageBufferBindingSize}}.
:: |resource|.{{GPUBufferBinding/offset}} is a multiple of
|limits|.{{supported limits/minStorageBufferOffsetAlignment}}.
</dl>
</dl>
</div>
Then:
1. Generate a {{GPUValidationError}} in the current scope with appropriate
error message.
1. Make |bindGroup| [=invalid=] and return |bindGroup|.
1. Let |bindGroup|.{{GPUBindGroup/[[layout]]}} =
|descriptor|.{{GPUBindGroupDescriptor/layout}}.
1. Let |bindGroup|.{{GPUBindGroup/[[entries]]}} =
|descriptor|.{{GPUBindGroupDescriptor/entries}}.
1. Let |bindGroup|.{{GPUBindGroup/[[usedResources]]}} = {}.
1. For each {{GPUBindGroupEntry}} |bindingDescriptor| in
|descriptor|.{{GPUBindGroupDescriptor/entries}}:
1. Let |internalUsage| be the [=binding usage=] for |layoutBinding|.
1. Each [=subresource=] seen by |resource| is added to {{GPUBindGroup/[[usedResources]]}} as |internalUsage|.
</div>
1. Return |bindGroup|.
Issue: define the "effective buffer binding size" separately.
</div>
</dl>
## <dfn interface>GPUPipelineLayout</dfn> ## {#pipeline-layout}
A {{GPUPipelineLayout}} defines the mapping between resources of all {{GPUBindGroup}} objects set up during command encoding in {{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup}}, and the shaders of the pipeline set by {{GPURenderEncoderBase/setPipeline(pipeline)|GPURenderEncoderBase.setPipeline}} or {{GPUComputePassEncoder/setPipeline(pipeline)|GPUComputePassEncoder.setPipeline}}.
The full binding address of a resource can be defined as a trio of:
1. shader stage mask, to which the resource is visible
2. bind group index
3. binding number
The components of this address can also be seen as the binding space of a pipeline. A {{GPUBindGroup}} (with the corresponding {{GPUBindGroupLayout}}) covers that space for a fixed bind group index. The contained bindings need to be a superset of the resources used by the shader at this bind group index.
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUPipelineLayout {
};
GPUPipelineLayout includes GPUObjectBase;
</script>
{{GPUPipelineLayout}} has the following internal slots:
<dl dfn-type=attribute dfn-for="GPUPipelineLayout">
: <dfn>\[[bindGroupLayouts]]</dfn> of type [=list=]&lt;{{GPUBindGroupLayout}}&gt;.
::
The {{GPUBindGroupLayout}} objects provided at creation in {{GPUPipelineLayoutDescriptor/bindGroupLayouts|GPUPipelineLayoutDescriptor.bindGroupLayouts}}.
</dl>
Note: using the same {{GPUPipelineLayout}} for many {{GPURenderPipeline}} or {{GPUComputePipeline}} pipelines guarantees that the user agent doesn't need to rebind any resources internally when there is a switch between these pipelines.
<div class="example">
{{GPUComputePipeline}} object X was created with {{GPUPipelineLayout/[[bindGroupLayouts]]|GPUPipelineLayout.bindGroupLayouts}} A, B, C. {{GPUComputePipeline}} object Y was created with {{GPUPipelineLayout/[[bindGroupLayouts]]|GPUPipelineLayout.bindGroupLayouts}} A, D, C. Supposing the command encoding sequence has two dispatches:
1. {{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup(0, ...)}}
1. {{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup(1, ...)}}
1. {{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup(2, ...)}}
1. {{GPUComputePassEncoder/setPipeline(pipeline)|setPipeline(X)}}
1. {{GPUComputePassEncoder/dispatch(x, y, z)|dispatch()}}
1. {{GPUProgrammablePassEncoder/setBindGroup(index, bindGroup, dynamicOffsets)|setBindGroup(1, ...)}}
1. {{GPUComputePassEncoder/setPipeline(pipeline)|setPipeline(Y)}}
1. {{GPUComputePassEncoder/dispatch(x, y, z)|dispatch()}}
In this scenario, the user agent would have to re-bind the group slot 2 for the second dispatch, even though neither the {{GPUBindGroupLayout}} at index 2 of {{GPUPipelineLayout/[[bindGroupLayouts]]|GPUPipelineLayout.bindGrouplayouts}}, or the {{GPUBindGroup}} at slot 2, change.
</div>
Issue: should this example and the note be moved to some "best practices" document?
Note: the expected usage of the {{GPUPipelineLayout}} is placing the most common and the least frequently changing bind groups at the "bottom" of the layout, meaning lower bind group slot numbers, like 0 or 1. The more frequently a bind group needs to change between draw calls, the higher its index should be. This general guideline allows the user agent to minimize state changes between draw calls, and consequently lower the CPU overhead.
### Creation ### {#pipeline-layout-creation}
A {{GPUPipelineLayout}} is created via {{GPUDevice/createPipelineLayout()|GPUDevice.createPipelineLayout()}}.
<script type=idl>
dictionary GPUPipelineLayoutDescriptor : GPUObjectDescriptorBase {
required sequence<GPUBindGroupLayout> bindGroupLayouts;
};
</script>
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>createPipelineLayout(descriptor)</dfn>
::
Creates a {{GPUPipelineLayout}}.
<div algorithm=GPUDevice.createPipelineLayout>
**Called on:** {{GPUDevice}} |this|.
**Arguments:**
<pre class=argumentdef for="GPUDevice/createPipelineLayout(descriptor)">
|descriptor|: Description of the {{GPUPipelineLayout}} to create.
</pre>
**Returns:** {{GPUPipelineLayout}}
1. If any of the following requirements are unmet:
<div class=validusage>
Let |limits| be |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.
Let |allEntries| be the result of concatenating
|bgl|.{{GPUBindGroupLayout/[[descriptor]]}}.{{GPUBindGroupLayoutDescriptor/entries}}
for all |bgl| in |descriptor|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}}.
- Every {{GPUBindGroupLayout}} in |descriptor|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}}
must be [$valid to use with$] |this|.
- The [=list/size=] of |descriptor|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}}
must be &le; |limits|.{{supported limits/maxBindGroups}}.
- |allEntries| must not [=exceeds the binding slot limits|exceed the binding slot limits=] of |limits|.
</div>
Then:
1. Generate a {{GPUValidationError}} in the current scope with appropriate error message.
1. Create a new [=invalid=] {{GPUPipelineLayout}} and return the result.
1. Let |pl| be a new {{GPUPipelineLayout}} object.
1. Set the |pl|.{{GPUPipelineLayout/[[bindGroupLayouts]]}} to
|descriptor|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}}.
1. Return |pl|.
</div>
</dl>
Note: two {{GPUPipelineLayout}} objects are considered equivalent for any usage
if their internal {{GPUPipelineLayout/[[bindGroupLayouts]]}} sequences contain
{{GPUBindGroupLayout}} objects that are [=group-equivalent=].
# Shader Modules # {#shader-modules}
## <dfn interface>GPUShaderModule</dfn> ## {#shader-module}
<script type=idl>
[Exposed=(Window, DedicatedWorker)]
interface GPUShaderModule {
Promise<GPUCompilationInfo> compilationInfo();
};
GPUShaderModule includes GPUObjectBase;
</script>
{{GPUShaderModule}} is {{Serializable}}. It is a reference to an internal
shader module object, and {{Serializable}} means that the reference can be
*copied* between realms (threads/workers), allowing multiple realms to access
it concurrently. Since {{GPUShaderModule}} is immutable, there are no race
conditions.
Issue(gpuweb/gpuweb#354): Finish defining multithreading API and add `[Serializable]` back to the interface.
### Shader Module Creation ### {#shader-module-creation}
<script type=idl>
dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase {
required USVString code;
object sourceMap;
};
</script>
{{GPUShaderModuleDescriptor/sourceMap}}, if defined, MAY be interpreted as a
source-map-v3 format.
Source maps are optional, but serve as a standardized way to support dev-tool
integration such as source-language debugging. [[SourceMap]]
<dl dfn-type=method dfn-for=GPUDevice>
: <dfn>createShaderModule(descriptor)</dfn>
::
Creates a {{GPUShaderModule}}.
<div algorithm=GPUDevice.createShaderModule>
**Called on:** {{GPUDevice}} this.
**Arguments:**
<pre class=argumentdef for="GPUDevice/createShaderModule(descriptor)">
descriptor: Description of the {{GPUShaderModule}} to create.
</pre>
**Returns:** {{GPUShaderModule}}
Issue: Describe {{GPUDevice/createShaderModule()}} algorithm steps.
</div>
</dl>
### Shader Module Compilation Information ### {#shader-module-compilation-information}
<script type=idl>
enum GPUCompilationMessageType {
"error",
"warning",
"info"
};
[Exposed=(Window, DedicatedWorker), Serializable]
interface GPUCompilationMessage {
readonly attribute DOMString message;
readonly attribute GPUCompilationMessageType type;
readonly attribute unsigned long long lineNum;
readonly attribute unsigned long long linePos;
readonly attribute unsigned long long offset;
readonly attribute unsigned long long length;
};
[Exposed=(Window, DedicatedWorker), Serializable]
interface GPUCompilationInfo {
readonly attribute FrozenArray<GPUCompilationMessage> messages;
};