Dictionaries and Unions¶
Typed adapter feature level¶
WebIDL declares GPURequestAdapterOptions.featureLevel as DOMString. The portable Kotlin API
instead uses GPUFeatureLevel, with Core as its default and Compatibility as the other value:
RequestAdapterOptions(featureLevel = GPUFeatureLevel.Compatibility)
The browser mapper sends "core" or "compatibility"; raw WGPURequestAdapterOptions.featureLevel
remains a string. Native actual values follow WGPUFeatureLevel (Compatibility = 1u, Core = 2u).
This is a source/ABI-breaking change for callers or implementations using String.
An adapter can advertise Core capabilities even when requested in Compatibility mode. Device
features govern validation: without explicitly requesting features, Compatibility enables none,
whereas Core enables CoreFeaturesAndLimits. An implementation unable to enforce Compatibility
restrictions may default to Core, so a Compatibility request alone does not prove those restrictions
are active.
Union type handling¶
"Dict" type treatment¶
WebGPU uses union types in some cases, particularly with sequences and dictionary types. To simplify the implementation and ensure type safety, these union types are transformed into single dictionary types.
Example: GPUColor¶
In the WebIDL specification, GPUColor is defined as a union type:
typedef (sequence<double> or GPUColorDict) GPUColor;
In our implementation, this is transformed into a single interface:
// From interfaces.kt
interface GPUColor {
val r: Double
val g: Double
val b: Double
val a: Double
}
This transformation offers several benefits:
- Type safety — the properties are explicitly defined with their types.
- Clarity — the interface clearly communicates the expected structure.
- Consistency — all implementations must provide the same properties.
- Platform agnosticism — the interface can be implemented on any platform.
Implementation in different modules¶
In the core module, the interface is defined with Kotlin types:
// From interfaces.kt in webgpu-api
interface GPUColor {
val r: Double
val g: Double
val b: Double
val a: Double
}
In the binding module, the interface is defined with JavaScript types:
// From types.kt in webgpu-web-bindings
external interface WGPUColor : JsAny {
var r: JsNumber /* double */
var g: JsNumber /* double */
var b: JsNumber /* double */
var a: JsNumber /* double */
}
This approach allows for platform-specific implementations while maintaining a consistent API across all platforms.
WebIDL records and nullable values¶
A WebIDL record<K, V> is not a Kotlin Map. The generated binding represents it as
WebGpuRecord, an external JsAny created with createWebGpuRecord() (a null-prototype object)
and filled with setRecordValue(). This keeps the dictionary a plain JavaScript object with
enumerable own properties, as the browser APIs require, instead of a Map instance. It is used for
pipeline constants and requested/limit dictionaries.
Nullable WebIDL members are preserved through the JavaScript boundary. A nullable binding member is
generated as a nullable parameter or result (WGPUBindGroup?, WGPUBuffer?), and a Promise whose
value is nullable becomes Promise<JsAny?>. Browser wrappers therefore pass null through rather
than coercing it, and the asynchronous entry points return Result values that distinguish an
absent value, a rejected promise, and a cancelled coroutine.
Dictionary and interface types¶
Interface-based implementation¶
Dictionary types and interfaces in WebGPU are transformed into Kotlin interface constructs. This
design choice provides several advantages:
- Platform agnosticism — interfaces can be implemented differently on each platform while maintaining a consistent API.
- Extensibility — new functionality can be added through extension functions or interface inheritance.
- Type safety — the compiler ensures that all required properties and methods are implemented.
- Flexibility — implementations can be tailored to specific platform requirements.
Core implementation¶
The core module (webgpu-api) defines platform-agnostic interfaces with Kotlin types:
// From interfaces.kt
interface GPUBufferDescriptor {
val size: GPUSize64
val usage: GPUBufferUsageFlags
val mappedAtCreation: Boolean
}
interface GPUDevice : GPUObjectBase, AutoCloseable {
val features: GPUSupportedFeatures
val limits: GPUSupportedLimits
val adapterInfo: GPUAdapterInfo
val queue: GPUQueue
fun createBuffer(descriptor: GPUBufferDescriptor): GPUBuffer
// Other methods...
}
Web-specific implementation¶
The binding module (webgpu-web-bindings) defines JavaScript-specific interfaces with the WGPU
prefix:
// From types.kt
external interface WGPUBufferDescriptor : JsAny, WGPUObjectDescriptorBase {
var size: JsNumber /* GPUSize64 */
var usage: JsNumber /* GPUBufferUsageFlags */
var mappedAtCreation: Boolean
}
external interface WGPUDevice : JsAny, WGPUObjectBase {
var features: JsAny /* GPUSupportedFeatures */
var limits: WGPUSupportedLimits /* GPUSupportedLimits */
var adapterInfo: WGPUAdapterInfo /* GPUAdapterInfo */
var queue: WGPUQueue /* GPUQueue */
fun createBuffer(descriptor: WGPUBufferDescriptor /* GPUBufferDescriptor */): WGPUBuffer
// Other methods...
}
Extensibility¶
This interface-based approach allows for different implementations and extensions:
- Basic implementations — an implementation provides straightforward versions of these interfaces.
- Advanced implementations — other projects can extend these interfaces to provide domain-specific languages (DSLs) or rich utility methods.
- Custom extensions — developers can add extension functions without modifying the core interfaces.
Default values¶
Each implementation must respect the default values specified in the WebIDL definitions to ensure consistency with the WebGPU standard. This approach maintains compliance and avoids discrepancies across different platforms.
For example, if a WebIDL property has a default value:
dictionary GPUSamplerDescriptor {
GPUAddressMode addressModeU = "clamp-to-edge";
// Other properties...
};
The Kotlin implementation should respect this default:
interface GPUSamplerDescriptor {
val addressModeU: GPUAddressMode get() = GPUAddressMode.ClampToEdge
// Other properties...
}