Problem
WebGPU is a good match for modern explicit graphics APIs such as Vulkan, Metal and D3D12. However, there are a large number of devices which do not yet support those APIs. In particular, on Chrome on Windows, 31% of Chrome users do not have D3D11.1 or higher. On Android, 23% of Android users do not have Vulkan 1.1 (15% do not have Vulkan at all). On ChromeOS, Vulkan penetration is still quite low, while OpenGL ES 3.1 is ubiquitous.
Goals
The primary goal of WebGPU Compatibility mode is to increase the reach of WebGPU by providing an opt-in, slightly restricted subset of WebGPU which will run on older APIs such as D3D11 and OpenGL ES. This will increase adoption of WebGPU applications via a wider userbase.
Since WebGPU Compatibility mode is a subset of WebGPU, all valid Compatibility mode applications are also valid WebGPU applications. Consequently, Compatibility mode applications will also run on user agents which do not support Compatibility mode. Such user agents will simply ignore the option requesting a Compatibility mode Adapter and return a Core WebGPU Adapter instead.
WebGPU Spec Changes
partial dictionary GPURequestAdapterOptions {
boolean compatibilityMode = false;
}
When calling GPU.RequestAdapter(), passing compatibilityMode = true in the GPURequestAdapterOptions will indicate to the User Agent to select the Compatibility subset of WebGPU. Any Devices created from the resulting Adapter on supporting UAs will support only Compatibility mode. Calls to APIs unsupported by Compatibility mode will result in validation errors.
Note that a supporting User Agent may return a compatibilityMode = true Adapter which is backed by a fully WebGPU-capable hardware adapter, such as D3D12, Metal or Vulkan, so long as it validates all subsequent API calls made on the Adapter and the objects it vends against the Compatibility subset.
partial interface GPUAdapter {
readonly attribute boolean isCompatibilityMode;
}
As a convenience to the developer, the Adapter returned will have the isCompatibilityMode property set to true.
partial dictionary GPUTextureDescriptor {
GPUTextureViewDimension textureBindingViewDimension;
}
See "Texture view dimension can be specified", below.
Compatibility mode restrictions
1. Texture view dimension may be specified
When specifying a texture, a textureBindingViewDimension property determines the views which can be bound from that texture for sampling (see "Proposed IDL changes", above). Binding a view of a different dimension for sampling than specified at texture creation time will cause a validation error. If textureBindingViewDimension is unspecified, use the same algorithm as createView():
if desc.dimension is "1d":
set textureBindingViewDimension to "1d"
if desc.dimension is "2d":
if desc.size.depthOrArrayLayers is 1:
set textureBindingViewDimension to "2d"
else:
set textureBindingViewDimension to "2d-array"
if desc.dimension is "3d":
set textureBindingViewDimension to "3d"
Justification: OpenGL ES does not support texture views.
Alternatives considered:
-
add viewDimension to GPUTextureDescriptor, as above, but make it mandatory; not specifying a viewDimension is a validation error
- pros:
- ensures no unexpected behaviour from existing apps
- cons:
- more verbose than a default
-
make a view dimension guess at texture creation time, and perform a texture-to-texture copy at bind time if the guess was incorrect.
- pros:
- wider support of existing WebGPU content without modification
- cons:
- unexpected performance cliff for developers
- potentially increased VRAM usage (two+ copies of texture data)
-
make a view dimension guess at texture creation time, and perform a texture-to-texture copy on first binding if the guess was incorrect. All subsequent views bound for sampling from that texture must have the same dimension as the first use, else a validation error occurs.
- pros:
- wider support of existing WebGPU content without modification
- at worst, a one-time performance penalty
- cons:
- unexpected performance cliff for developers
- potentially increased VRAM usage (at worst, two copies of texture data)
-
disallow 6-layer 2D arrays (always cube maps)
- cons:
- poor compatibility, limits applications
-
disallow cube maps (always create 6-layer 2D arrays)
- cons:
- poor compatibility, limits applications
2. Disallow CommandEncoder.copyTextureToBuffer() and CommandEncoder.copyTextureToTexture() for compressed texture formats
CommandEncoder.copyTextureToBuffer() and CommandEncoder.copyTextureToTexture() of a compressed texture is disallowed, and will result in a validation error.
Justification: Compressed texture formats are non-renderable in OpenGL ES, and
glReadPixels() on works on a framebuffer-complete FBO. Additionally, because ES 3.1 does not support glCopyImageSubData(), texture-to-texture copies must be worked around with glBlitFramebuffer(). Since compressed textures cannot be bound for rendering, they cannot use the glBlitFramebuffer() workaround.
Alternatives considered:
- implement a shadow copy buffer, and upload the compressed data to both a buffer and a texture
- pros:
- cons:
- performance overhead, even when readbacks are not required
- VRAM overhead
3. Views of the same texture used in a single draw may not differ in mip level or array layer parameters.
A draw call may not reference the same texture with two views differing in baseMipLevel, mipLevelCount, baseArrayLayer, or arrayLayerCount. Only a single mip level range and array layer range per texture is supported. This is enforced via validation at encode time.
Justification: OpenGL ES does not support texture views.
Alternatives considered:
- when two bindings exist with different mip levels or array layers, do a texture-to-texture copy
- pros:
- cons:
- a performance cliff for developers
- higher VRAM usage
4. Color state alphaBlend, colorBlend and writeMask may not differ between color attachments in a single draw.
Color state descriptors used in a single draw must have the same alphaBlend, colorBlend and writeMask, or else an encode-time validation error will occur.
Justification: OpenGL ES 3.1 does not support indexed draw buffer state.
Alternatives considered
- require
GL_EXT_draw_buffers_indexed
- expose as a WebGPU extension when the OpenGL ES extension is present (this could be a followup change)
- pros:
- ease of implementation
- good performance
- cons:
- if this is the only implementation, it has poor reach
5. Disallow sample_mask builtin in WGSL.
Justification: OpenGL ES 3.1 does not support gl_SampleMask, gl_SampleMaskIn.
Alternatives considered
- require
GL_OES_sample_variables
- expose as a WebGPU extension when the OpenGL ES extension is present (this could be a followup change)
- pros:
- cons:
- poor reach, unless this is built on top of the proposed solution
6. Disallow GPUTextureViewDimension "CubeArray" via validation
Justification: OpenGL ES does not support Cube Array textures.
Alternatives Considered:
7. Disallow textureLoad() of depth textures in WGSL via validation.
Justification: OpenGL ES does not support texelFetch() of a depth texture.
Alternatives considered:
- bind to an RGBA8 binding point and use shader ALU
- pros:
- compatibility, performance
- cons:
- untried (does this work?)
- use
texture() with quantized texture coordinates; massage the results
- pros:
- compatibility, performance
- cons:
- untried
- complexity of implementation
8. Disallow texture*() of a texture_depth_2d_array with an offset
Justification: OpenGL ES does not support textureOffset() on a sampler2DArrayShadow.
Alternatives considered:
- emulate with a
texture() call and use ALU for offset
- pros:
- compatibility, performance
- cons:
9. Emit dpdx() and dpdy() for all derivative functions (include Coarse and Fine variants).
Justification: GLSL does not support dFd*Coarse() or dFd*Fine() functions. However, these variants can be interpreted as a hint in WGSL, and emitted as dFd*().
Alternatives considered:
- disallow
Coarse and Fine variants via validation in WGSL
Coarse is allowed; Fine is disallowed via validation
10. Disallow bgra8unorm-srgb textures.
Justification: OpenGL ES does not support sRGB BGRA texture formats.
Alternatives considered:
- use a compute shader to swizzle bgra8unorm-srgb to rgba8unorm-srgb on
copyBufferToTexture() and the reverse on copyTextureToBuffer()
- pros:
- cons:
- a performance cliff for developers
- increased VRAM usage
Compatibility mode workarounds
The features below are not supported natively in OpenGL ES, but it is proposed to implement them in the User Agent via workarounds.
1. Emulate copyTextureToBuffer() of depth/stencil textures with a compute shader
Justification: OpenGL ES does not support glReadPixels() of depth/stencil textures.
Alternatives considered:
- use CPU readback and re-upload
- disallow via validation
- require
GL_NV_read_depth_stencil
- pros:
- cons:
- poor support (<1% on gpuinfo.org)
2. Emulate copyTextureToBuffer() of SNORM textures with a compute shader
Justification: OpenGL ES does not support glReadPixels() of SNORM textures
Alternatives considered:
- disallow via validation
- pros:
- cons:
- poor compatibility; limits applications
3. Emulate separate sampler and texture objects with a cache of combined texture/samplers.
Justification: OpenGL ES does not support separate sampler and texture objects.
Alternatives considered:
- allow only a single sampler to be used with a given texture
4. Inject hidden uniforms for textureNumLevels() and textureNumSamples() where required.
Justification: OpenGL ES 3.1 does not support textureQueryLevels() (only added to desktop GL in OpenGL 4.3).
Alternatives Considered:
- disallow
textureNumLevels() and textureNumSamples() in WGSL via validation.
5. Emulate 1D textures with 2D textures.
Justification: OpenGL ES does not support 1D textures.
Alternatives Considered:
- disallow 1D textures in WGSL and API
6. Manually pad out GLSL structs and interface blocks to support explicit @align or @size decorations.
Justification: OpenGL ES does not support offset= interface block decorations on anything but atomic_uint.
Alternatives considered:
- disallow
@align and @size on WGSL structs via validation
7. Use GL_ext_texture_format_BGRA8888 to support BGRA copyBufferToTexture() and swizzle workarounds and RGBA textures where unavailable.
Justification: OpenGL ES does not support BGRA texture formats.
GL_ext_texture_format_BGRA8888 supports texture uploads and the BGRA8888 texture format, and has 99%+ support. The vast majority of devices which do not support it are GLES 3.0 implementations, and so would not support Compatibility mode anyway, but if an important device emerges, a CPU- or GPU-based swizzle workaround and RGBA textures should be implemented.
Alternatives considered
- disallow BGRA8888 as a texture format through validation
8. Work around lack of BGRA support in copyTextureToBuffer() via compute or sampling.
Justification: OpenGL ES does not support BGRA texture formats for glReadPixels(), even with the GL_ext_texture_format_BGRA8888 extension.
Alternatives considered:
- disallow copyTextureToBuffer() for BGRA formats.
9. Use emulation workaround to support BaseVertex / BaseInstance in direct draws. Disallow via validation in indirect draws.
Justification: OpenGL ES 3.1 does not support baseVertex or baseInstance parameters in Draw calls.
Alternatives considered
- require
OES_draw_elements_base_vertex (21% support) or EXT_draw_elements_base_vertex (21%) and GL_EXT_base_instance (1.7%)
Problem
WebGPU is a good match for modern explicit graphics APIs such as Vulkan, Metal and D3D12. However, there are a large number of devices which do not yet support those APIs. In particular, on Chrome on Windows, 31% of Chrome users do not have D3D11.1 or higher. On Android, 23% of Android users do not have Vulkan 1.1 (15% do not have Vulkan at all). On ChromeOS, Vulkan penetration is still quite low, while OpenGL ES 3.1 is ubiquitous.
Goals
The primary goal of WebGPU Compatibility mode is to increase the reach of WebGPU by providing an opt-in, slightly restricted subset of WebGPU which will run on older APIs such as D3D11 and OpenGL ES. This will increase adoption of WebGPU applications via a wider userbase.
Since WebGPU Compatibility mode is a subset of WebGPU, all valid Compatibility mode applications are also valid WebGPU applications. Consequently, Compatibility mode applications will also run on user agents which do not support Compatibility mode. Such user agents will simply ignore the option requesting a Compatibility mode Adapter and return a Core WebGPU Adapter instead.
WebGPU Spec Changes
partial dictionary GPURequestAdapterOptions { boolean compatibilityMode = false; }When calling
GPU.RequestAdapter(), passingcompatibilityMode = truein theGPURequestAdapterOptionswill indicate to the User Agent to select the Compatibility subset of WebGPU. Any Devices created from the resulting Adapter on supporting UAs will support only Compatibility mode. Calls to APIs unsupported by Compatibility mode will result in validation errors.Note that a supporting User Agent may return a
compatibilityMode = trueAdapter which is backed by a fully WebGPU-capable hardware adapter, such as D3D12, Metal or Vulkan, so long as it validates all subsequent API calls made on the Adapter and the objects it vends against the Compatibility subset.As a convenience to the developer, the Adapter returned will have the
isCompatibilityModeproperty set totrue.partial dictionary GPUTextureDescriptor { GPUTextureViewDimension textureBindingViewDimension; }See "Texture view dimension can be specified", below.
Compatibility mode restrictions
1. Texture view dimension may be specified
When specifying a texture, a
textureBindingViewDimensionproperty determines the views which can be bound from that texture for sampling (see "Proposed IDL changes", above). Binding a view of a different dimension for sampling than specified at texture creation time will cause a validation error. IftextureBindingViewDimensionis unspecified, use the same algorithm ascreateView():Justification: OpenGL ES does not support texture views.
Alternatives considered:
add
viewDimensiontoGPUTextureDescriptor, as above, but make it mandatory; not specifying a viewDimension is a validation errormake a view dimension guess at texture creation time, and perform a texture-to-texture copy at bind time if the guess was incorrect.
make a view dimension guess at texture creation time, and perform a texture-to-texture copy on first binding if the guess was incorrect. All subsequent views bound for sampling from that texture must have the same dimension as the first use, else a validation error occurs.
disallow 6-layer 2D arrays (always cube maps)
disallow cube maps (always create 6-layer 2D arrays)
2. Disallow
CommandEncoder.copyTextureToBuffer()andCommandEncoder.copyTextureToTexture()for compressed texture formatsCommandEncoder.copyTextureToBuffer()andCommandEncoder.copyTextureToTexture()of a compressed texture is disallowed, and will result in a validation error.Justification: Compressed texture formats are non-renderable in OpenGL ES, and
glReadPixels() on works on a framebuffer-complete FBO. Additionally, because ES 3.1 does not support glCopyImageSubData(), texture-to-texture copies must be worked around with glBlitFramebuffer(). Since compressed textures cannot be bound for rendering, they cannot use the glBlitFramebuffer() workaround.
Alternatives considered:
3. Views of the same texture used in a single draw may not differ in mip level or array layer parameters.
A draw call may not reference the same texture with two views differing in
baseMipLevel,mipLevelCount,baseArrayLayer, orarrayLayerCount. Only a single mip level range and array layer range per texture is supported. This is enforced via validation at encode time.Justification: OpenGL ES does not support texture views.
Alternatives considered:
4. Color state
alphaBlend,colorBlendandwriteMaskmay not differ between color attachments in a single draw.Color state descriptors used in a single draw must have the same alphaBlend, colorBlend and writeMask, or else an encode-time validation error will occur.
Justification: OpenGL ES 3.1 does not support indexed draw buffer state.
Alternatives considered
GL_EXT_draw_buffers_indexedGL_EXT_draw_buffers_indexedhas limited support (~42%)5. Disallow
sample_maskbuiltin in WGSL.Justification: OpenGL ES 3.1 does not support
gl_SampleMask,gl_SampleMaskIn.Alternatives considered
GL_OES_sample_variablesGL_OES_sample_variableshas limited support (~48%)6. Disallow
GPUTextureViewDimension"CubeArray"via validationJustification: OpenGL ES does not support Cube Array textures.
Alternatives Considered:
7. Disallow
textureLoad()of depth textures in WGSL via validation.Justification: OpenGL ES does not support
texelFetch()of a depth texture.Alternatives considered:
texture()with quantized texture coordinates; massage the results8. Disallow
texture*()of atexture_depth_2d_arraywith an offsetJustification: OpenGL ES does not support
textureOffset()on a sampler2DArrayShadow.Alternatives considered:
texture()call and use ALU for offset9. Emit
dpdx()anddpdy()for all derivative functions (include Coarse and Fine variants).Justification: GLSL does not support
dFd*Coarse()ordFd*Fine()functions. However, these variants can be interpreted as a hint in WGSL, and emitted asdFd*().Alternatives considered:
CoarseandFinevariants via validation in WGSLCoarseis allowed;Fineis disallowed via validation10. Disallow bgra8unorm-srgb textures.
Justification: OpenGL ES does not support sRGB BGRA texture formats.
Alternatives considered:
copyBufferToTexture()and the reverse oncopyTextureToBuffer()Compatibility mode workarounds
The features below are not supported natively in OpenGL ES, but it is proposed to implement them in the User Agent via workarounds.
1. Emulate
copyTextureToBuffer()of depth/stencil textures with a compute shaderJustification: OpenGL ES does not support
glReadPixels()of depth/stencil textures.Alternatives considered:
GL_NV_read_depth_stencil2. Emulate
copyTextureToBuffer()of SNORM textures with a compute shaderJustification: OpenGL ES does not support
glReadPixels()of SNORM texturesAlternatives considered:
3. Emulate separate sampler and texture objects with a cache of combined texture/samplers.
Justification: OpenGL ES does not support separate sampler and texture objects.
Alternatives considered:
4. Inject hidden uniforms for
textureNumLevels()andtextureNumSamples()where required.Justification: OpenGL ES 3.1 does not support textureQueryLevels() (only added to desktop GL in OpenGL 4.3).
Alternatives Considered:
textureNumLevels()andtextureNumSamples()in WGSL via validation.5. Emulate 1D textures with 2D textures.
Justification: OpenGL ES does not support 1D textures.
Alternatives Considered:
6. Manually pad out GLSL structs and interface blocks to support explicit
@alignor@sizedecorations.Justification: OpenGL ES does not support offset= interface block decorations on anything but
atomic_uint.Alternatives considered:
@alignand@sizeon WGSL structs via validation7. Use
GL_ext_texture_format_BGRA8888to support BGRAcopyBufferToTexture()and swizzle workarounds and RGBA textures where unavailable.Justification: OpenGL ES does not support BGRA texture formats.
GL_ext_texture_format_BGRA8888supports texture uploads and the BGRA8888 texture format, and has 99%+ support. The vast majority of devices which do not support it are GLES 3.0 implementations, and so would not support Compatibility mode anyway, but if an important device emerges, a CPU- or GPU-based swizzle workaround and RGBA textures should be implemented.Alternatives considered
8. Work around lack of BGRA support in copyTextureToBuffer() via compute or sampling.
Justification: OpenGL ES does not support BGRA texture formats for
glReadPixels(), even with theGL_ext_texture_format_BGRA8888extension.Alternatives considered:
9. Use emulation workaround to support BaseVertex / BaseInstance in direct draws. Disallow via validation in indirect draws.
Justification: OpenGL ES 3.1 does not support
baseVertexorbaseInstanceparameters in Draw calls.Alternatives considered
OES_draw_elements_base_vertex(21% support) orEXT_draw_elements_base_vertex(21%) andGL_EXT_base_instance(1.7%)