• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / luma.gl / 27017296180

05 Jun 2026 01:20PM UTC coverage: 70.704% (+0.1%) from 70.564%
27017296180

push

github

web-flow
chore: Minor API completions and doc improvements (#2665)

8861 of 14167 branches covered (62.55%)

Branch coverage included in aggregate %.

174 of 196 new or added lines in 15 files covered. (88.78%)

1 existing line in 1 file now uncovered.

18353 of 24323 relevant lines covered (75.46%)

4341.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.91
/modules/webgpu/src/adapter/webgpu-adapter.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
// biome-ignore format: preserve layout
6
// / <reference types="@webgpu/types" />
7

8
import {
9
  Adapter,
10
  type DeviceProps,
11
  log
12
} from '@luma.gl/core';
13
import type {WebGPUDevice} from './webgpu-device';
14

15
type WebGPUSupportedLimitName = Exclude<keyof GPUSupportedLimits, '__brand'>;
16
type RequestedWebGPUFeatureLevel = NonNullable<DeviceProps['featureLevel']>;
17

18
const WEBGPU_SUPPORTED_LIMIT_NAMES: readonly WebGPUSupportedLimitName[] = [
98✔
19
  'maxTextureDimension1D',
20
  'maxTextureDimension2D',
21
  'maxTextureDimension3D',
22
  'maxTextureArrayLayers',
23
  'maxBindGroups',
24
  'maxBindGroupsPlusVertexBuffers',
25
  'maxBindingsPerBindGroup',
26
  'maxDynamicUniformBuffersPerPipelineLayout',
27
  'maxDynamicStorageBuffersPerPipelineLayout',
28
  'maxSampledTexturesPerShaderStage',
29
  'maxSamplersPerShaderStage',
30
  'maxStorageBuffersPerShaderStage',
31
  'maxStorageBuffersInVertexStage',
32
  'maxStorageBuffersInFragmentStage',
33
  'maxStorageTexturesPerShaderStage',
34
  'maxStorageTexturesInVertexStage',
35
  'maxStorageTexturesInFragmentStage',
36
  'maxUniformBuffersPerShaderStage',
37
  'maxUniformBufferBindingSize',
38
  'maxStorageBufferBindingSize',
39
  'minUniformBufferOffsetAlignment',
40
  'minStorageBufferOffsetAlignment',
41
  'maxVertexBuffers',
42
  'maxBufferSize',
43
  'maxVertexAttributes',
44
  'maxVertexBufferArrayStride',
45
  'maxInterStageShaderVariables',
46
  'maxColorAttachments',
47
  'maxColorAttachmentBytesPerSample',
48
  'maxComputeWorkgroupStorageSize',
49
  'maxComputeInvocationsPerWorkgroup',
50
  'maxComputeWorkgroupSizeX',
51
  'maxComputeWorkgroupSizeY',
52
  'maxComputeWorkgroupSizeZ',
53
  'maxComputeWorkgroupsPerDimension',
54
  'maxImmediateSize'
55
];
56

57
/**
58
 * Returns every WebGPU limit that luma.gl can request from an adapter.
59
 * @param supportedLimits Limits exposed by the selected WebGPU adapter.
60
 * @returns Limits to forward through `GPUDeviceDescriptor.requiredLimits`.
61
 */
62
export function getRequiredWebGPULimits(
63
  supportedLimits: GPUSupportedLimits
64
): Record<string, number> {
65
  const requiredLimits: Record<string, number> = {};
53✔
66

67
  for (const limitName of WEBGPU_SUPPORTED_LIMIT_NAMES) {
53✔
68
    const limitValue = supportedLimits[limitName];
1,908✔
69
    if (typeof limitValue === 'number') {
1,908✔
70
      requiredLimits[limitName] = limitValue;
1,874✔
71
    }
72
  }
73

74
  return requiredLimits;
53✔
75
}
76

77
/**
78
 * Returns the requested WebGPU feature level, defaulting to the portable core profile.
79
 * @param props Device creation props.
80
 * @returns Effective WebGPU feature level to request.
81
 */
82
export function getWebGPUFeatureLevel(props: DeviceProps): RequestedWebGPUFeatureLevel {
83
  return props.featureLevel ?? 'core';
55✔
84
}
85

86
/**
87
 * Returns WebGPU adapter options used while selecting an adapter.
88
 * @param props Device creation props.
89
 * @returns Options to pass to `navigator.gpu.requestAdapter()`.
90
 */
91
export function getWebGPURequestAdapterOptions(props: DeviceProps): GPURequestAdapterOptions {
92
  const options: GPURequestAdapterOptions = {featureLevel: 'core'};
55✔
93

94
  if (props.powerPreference && props.powerPreference !== 'default') {
55✔
95
    options.powerPreference = props.powerPreference;
54✔
96
  }
97

98
  return options;
55✔
99
}
100

101
/**
102
 * Returns adapter features required for the requested WebGPU feature level.
103
 * @param supportedFeatures Features exposed by the selected WebGPU adapter.
104
 * @param featureLevel Effective WebGPU feature level to request.
105
 * @returns Features to forward through `GPUDeviceDescriptor.requiredFeatures`.
106
 */
107
export function getRequiredWebGPUFeatures(
108
  supportedFeatures: GPUSupportedFeatures,
109
  featureLevel: RequestedWebGPUFeatureLevel
110
): GPUFeatureName[] {
111
  if (featureLevel === 'max') {
55✔
112
    return Array.from(supportedFeatures) as GPUFeatureName[];
53✔
113
  }
114

115
  return [];
2✔
116
}
117

118
export class WebGPUAdapter extends Adapter {
119
  /** type of device's created by this adapter */
120
  readonly type: WebGPUDevice['type'] = 'webgpu';
98✔
121
  protected gpuAdapterPromises = new Map<string, Promise<GPUAdapter | null>>();
98✔
122

123
  isSupported(): boolean {
124
    // Check if WebGPU is available
125
    return Boolean(typeof navigator !== 'undefined' && navigator.gpu);
2✔
126
  }
127

128
  isDeviceHandle(handle: unknown): boolean {
129
    if (typeof GPUDevice !== 'undefined' && handle instanceof GPUDevice) {
×
130
      return true;
×
131
    }
132

133
    // TODO - WebGPU does not yet seem to have a stable in-browser API, so we "sniff" for members instead
134
    if ((handle as any)?.queue) {
×
135
      return true;
×
136
    }
137

138
    return false;
×
139
  }
140

141
  async create(props: DeviceProps): Promise<WebGPUDevice> {
142
    if (!navigator.gpu) {
53!
143
      throw new Error('WebGPU not available. Recent Chrome browsers should work.');
×
144
    }
145

146
    const requestedFeatureLevel = getWebGPUFeatureLevel(props);
53✔
147
    const requestAdapterOptions = getWebGPURequestAdapterOptions(props);
53✔
148
    const gpuAdapterCacheKey = this.getGPUAdapterCacheKey(
53✔
149
      requestedFeatureLevel,
150
      requestAdapterOptions
151
    );
152
    const adapterPromise = this.getGPUAdapterPromise(gpuAdapterCacheKey, requestAdapterOptions);
53✔
153

154
    const adapter = await adapterPromise;
53✔
155

156
    if (!adapter) {
53!
157
      throw new Error('Failed to request WebGPU adapter');
×
158
    }
159

160
    //  Note: adapter.requestAdapterInfo() has been replaced with adapter.info. Fall back in case adapter.info is not available
161
    const adapterInfo =
162
      adapter.info ||
53!
163
      // @ts-ignore
164
      (await adapter.requestAdapterInfo?.());
165
    // log.probe(2, 'Adapter available', adapterInfo)();
166

NEW
167
    const deviceDescriptor: GPUDeviceDescriptor = {};
×
168

NEW
169
    const requiredFeatures = getRequiredWebGPUFeatures(adapter.features, requestedFeatureLevel);
×
NEW
170
    if (requiredFeatures.length > 0) {
×
171
      deviceDescriptor.requiredFeatures = requiredFeatures;
52✔
172
    }
173

174
    if (requestedFeatureLevel === 'max') {
53✔
175
      deviceDescriptor.requiredLimits = getRequiredWebGPULimits(adapter.limits);
52✔
176
    }
177

178
    const gpuDevice = await adapter.requestDevice(deviceDescriptor);
53✔
179
    this.gpuAdapterPromises.delete(gpuAdapterCacheKey);
53✔
180

181
    // log.probe(1, 'GPUDevice available')();
182

183
    const {WebGPUDevice} = await import('./webgpu-device');
53✔
184
    const deviceProps = {...props, featureLevel: requestedFeatureLevel};
53✔
185

186
    log.groupCollapsed(1, 'WebGPUDevice created')();
53✔
187
    try {
53✔
188
      const device = new WebGPUDevice(deviceProps, gpuDevice, adapter, adapterInfo);
53✔
189
      log.probe(
53✔
190
        1,
191
        'Device created. For more info, set chrome://flags/#enable-webgpu-developer-features'
192
      )();
193
      log.table(1, device.info)();
53✔
194
      return device;
53✔
195
    } finally {
196
      log.groupEnd(1)();
53✔
197
    }
198
  }
199

200
  async attach(handle: GPUDevice): Promise<WebGPUDevice> {
201
    throw new Error('WebGPUAdapter.attach() not implemented');
×
202
  }
203

204
  protected getGPUAdapterPromise(
205
    cacheKey: string,
206
    requestAdapterOptions: GPURequestAdapterOptions
207
  ): Promise<GPUAdapter | null> {
208
    let gpuAdapterPromise = this.gpuAdapterPromises.get(cacheKey);
53✔
209
    if (!gpuAdapterPromise) {
53!
210
      gpuAdapterPromise = navigator.gpu.requestAdapter(requestAdapterOptions);
53✔
211
      this.gpuAdapterPromises.set(cacheKey, gpuAdapterPromise);
53✔
212
    }
213
    return gpuAdapterPromise;
53✔
214
  }
215

216
  protected getGPUAdapterCacheKey(
217
    featureLevel: RequestedWebGPUFeatureLevel,
218
    requestAdapterOptions: GPURequestAdapterOptions
219
  ): string {
220
    return [
53✔
221
      featureLevel,
222
      requestAdapterOptions.featureLevel || 'core',
53!
223
      requestAdapterOptions.powerPreference || 'default'
53!
224
    ].join(':');
225
  }
226
}
227

228
export const webgpuAdapter = new WebGPUAdapter();
98✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc