@luma.gl/webgpu 9.2.6 → 9.3.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
- package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
- package/dist/adapter/resources/webgpu-fence.js +25 -0
- package/dist/adapter/resources/webgpu-fence.js.map +1 -0
- package/dist/adapter/resources/webgpu-texture.d.ts +12 -3
- package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture.js +143 -26
- package/dist/adapter/resources/webgpu-texture.js.map +1 -1
- package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
- package/dist/adapter/webgpu-adapter.js +34 -34
- package/dist/adapter/webgpu-adapter.js.map +1 -1
- package/dist/adapter/webgpu-canvas-context.d.ts +4 -3
- package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
- package/dist/adapter/webgpu-canvas-context.js +22 -21
- package/dist/adapter/webgpu-canvas-context.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +3 -1
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +14 -3
- package/dist/adapter/webgpu-device.js.map +1 -1
- package/dist/dist.dev.js +6438 -274
- package/dist/dist.min.js +10 -6
- package/dist/index.cjs +347 -86
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
- package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
- package/dist/wgsl/get-shader-layout-wgsl.js +136 -0
- package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
- package/package.json +5 -4
- package/src/adapter/resources/webgpu-fence.ts +30 -0
- package/src/adapter/resources/webgpu-texture.ts +202 -88
- package/src/adapter/webgpu-adapter.ts +40 -42
- package/src/adapter/webgpu-canvas-context.ts +25 -23
- package/src/adapter/webgpu-device.ts +19 -4
- package/src/index.ts +3 -0
- package/src/wgsl/get-shader-layout-wgsl.ts +156 -0
|
@@ -35,62 +35,60 @@ export class WebGPUAdapter extends Adapter {
|
|
|
35
35
|
throw new Error('WebGPU not available. Recent Chrome browsers should work.');
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// forceSoftware: false
|
|
43
|
-
});
|
|
38
|
+
const adapter = await navigator.gpu.requestAdapter({
|
|
39
|
+
powerPreference: 'high-performance'
|
|
40
|
+
// forceSoftware: false
|
|
41
|
+
});
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
if (!adapter) {
|
|
44
|
+
throw new Error('Failed to request WebGPU adapter');
|
|
45
|
+
}
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
47
|
+
// Note: adapter.requestAdapterInfo() has been replaced with adapter.info. Fall back in case adapter.info is not available
|
|
48
|
+
const adapterInfo =
|
|
49
|
+
adapter.info ||
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
(await adapter.requestAdapterInfo?.());
|
|
52
|
+
// log.probe(2, 'Adapter available', adapterInfo)();
|
|
53
|
+
|
|
54
|
+
const requiredFeatures: GPUFeatureName[] = [];
|
|
55
|
+
const requiredLimits: Record<string, number> = {};
|
|
56
|
+
|
|
57
|
+
if (props._requestMaxLimits) {
|
|
58
|
+
// Require all features
|
|
59
|
+
requiredFeatures.push(...(Array.from(adapter.features) as GPUFeatureName[]));
|
|
60
|
+
|
|
61
|
+
// Require all limits
|
|
62
|
+
// Filter out chrome specific keys (avoid crash)
|
|
63
|
+
const limits = Object.keys(adapter.limits).filter(
|
|
64
|
+
key => !['minSubgroupSize', 'maxSubgroupSize'].includes(key)
|
|
65
|
+
);
|
|
66
|
+
for (const key of limits) {
|
|
67
|
+
const limit = key as keyof GPUSupportedLimits;
|
|
68
|
+
const value = adapter.limits[limit];
|
|
69
|
+
if (typeof value === 'number') {
|
|
70
|
+
requiredLimits[limit] = value;
|
|
74
71
|
}
|
|
75
72
|
}
|
|
73
|
+
}
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
const gpuDevice = await adapter.requestDevice({
|
|
76
|
+
requiredFeatures,
|
|
77
|
+
requiredLimits
|
|
78
|
+
});
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
// log.probe(1, 'GPUDevice available')();
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
const {WebGPUDevice} = await import('./webgpu-device');
|
|
85
83
|
|
|
84
|
+
log.groupCollapsed(1, 'WebGPUDevice created')();
|
|
85
|
+
try {
|
|
86
86
|
const device = new WebGPUDevice(props, gpuDevice, adapter, adapterInfo);
|
|
87
|
-
|
|
88
87
|
log.probe(
|
|
89
88
|
1,
|
|
90
89
|
'Device created. For more info, set chrome://flags/#enable-webgpu-developer-features'
|
|
91
90
|
)();
|
|
92
91
|
log.table(1, device.info)();
|
|
93
|
-
|
|
94
92
|
return device;
|
|
95
93
|
} finally {
|
|
96
94
|
log.groupEnd(1)();
|
|
@@ -38,7 +38,7 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
38
38
|
|
|
39
39
|
// Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor
|
|
40
40
|
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
|
|
41
|
-
this.
|
|
41
|
+
this._configureDevice();
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** Destroy any textures produced while configured and remove the context configuration. */
|
|
@@ -47,14 +47,34 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
47
47
|
super.destroy();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// IMPLEMENTATION OF ABSTRACT METHODS
|
|
51
|
+
|
|
52
|
+
/** @see https://www.w3.org/TR/webgpu/#canvas-configuration */
|
|
53
|
+
_configureDevice(): void {
|
|
54
|
+
if (this.depthStencilAttachment) {
|
|
55
|
+
this.depthStencilAttachment.destroy();
|
|
56
|
+
this.depthStencilAttachment = null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Reconfigure the canvas size.
|
|
60
|
+
this.handle.configure({
|
|
61
|
+
device: this.device.handle,
|
|
62
|
+
format: this.device.preferredColorFormat,
|
|
63
|
+
// Can be used to define e.g. -srgb views
|
|
64
|
+
// viewFormats: [...]
|
|
65
|
+
colorSpace: this.props.colorSpace,
|
|
66
|
+
alphaMode: this.props.alphaMode
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
50
70
|
/** Update framebuffer with properly resized "swap chain" texture views */
|
|
51
|
-
|
|
71
|
+
_getCurrentFramebuffer(
|
|
52
72
|
options: {depthStencilFormat?: TextureFormatDepthStencil | false} = {
|
|
53
73
|
depthStencilFormat: 'depth24plus'
|
|
54
74
|
}
|
|
55
75
|
): WebGPUFramebuffer {
|
|
56
76
|
// Wrap the current canvas context texture in a luma.gl texture
|
|
57
|
-
const currentColorAttachment = this.
|
|
77
|
+
const currentColorAttachment = this._getCurrentTexture();
|
|
58
78
|
// TODO - temporary debug code
|
|
59
79
|
if (
|
|
60
80
|
currentColorAttachment.width !== this.drawingBufferWidth ||
|
|
@@ -80,28 +100,10 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
80
100
|
});
|
|
81
101
|
}
|
|
82
102
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
_updateDevice(): void {
|
|
86
|
-
if (this.depthStencilAttachment) {
|
|
87
|
-
this.depthStencilAttachment.destroy();
|
|
88
|
-
this.depthStencilAttachment = null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Reconfigure the canvas size.
|
|
92
|
-
// https://www.w3.org/TR/webgpu/#canvas-configuration
|
|
93
|
-
this.handle.configure({
|
|
94
|
-
device: this.device.handle,
|
|
95
|
-
format: this.device.preferredColorFormat,
|
|
96
|
-
// Can be used to define e.g. -srgb views
|
|
97
|
-
// viewFormats: [...]
|
|
98
|
-
colorSpace: this.props.colorSpace,
|
|
99
|
-
alphaMode: this.props.alphaMode
|
|
100
|
-
});
|
|
101
|
-
}
|
|
103
|
+
// PRIMARY METHODS
|
|
102
104
|
|
|
103
105
|
/** Wrap the current canvas context texture in a luma.gl texture */
|
|
104
|
-
|
|
106
|
+
_getCurrentTexture(): WebGPUTexture {
|
|
105
107
|
const handle = this.handle.getCurrentTexture();
|
|
106
108
|
return this.device.createTexture({
|
|
107
109
|
id: `${this.id}#color-texture`,
|
|
@@ -45,6 +45,9 @@ import {WebGPUCommandEncoder} from './resources/webgpu-command-encoder';
|
|
|
45
45
|
import {WebGPUCommandBuffer} from './resources/webgpu-command-buffer';
|
|
46
46
|
import {WebGPUQuerySet} from './resources/webgpu-query-set';
|
|
47
47
|
import {WebGPUPipelineLayout} from './resources/webgpu-pipeline-layout';
|
|
48
|
+
import {WebGPUFence} from './resources/webgpu-fence';
|
|
49
|
+
|
|
50
|
+
import {getShaderLayoutFromWGSL} from '../wgsl/get-shader-layout-wgsl';
|
|
48
51
|
|
|
49
52
|
/** WebGPU Device implementation */
|
|
50
53
|
export class WebGPUDevice extends Device {
|
|
@@ -136,15 +139,15 @@ export class WebGPUDevice extends Device {
|
|
|
136
139
|
return this._isLost;
|
|
137
140
|
}
|
|
138
141
|
|
|
142
|
+
getShaderLayout(source: string) {
|
|
143
|
+
return getShaderLayoutFromWGSL(source);
|
|
144
|
+
}
|
|
145
|
+
|
|
139
146
|
override isVertexFormatSupported(format: VertexFormat): boolean {
|
|
140
147
|
const info = this.getVertexFormatInfo(format);
|
|
141
148
|
return !info.webglOnly;
|
|
142
149
|
}
|
|
143
150
|
|
|
144
|
-
getTextureByteAlignment(): number {
|
|
145
|
-
return 1;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
151
|
createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WebGPUBuffer {
|
|
149
152
|
const newProps = this._normalizeBufferProps(props);
|
|
150
153
|
return new WebGPUBuffer(this, newProps);
|
|
@@ -196,6 +199,10 @@ export class WebGPUDevice extends Device {
|
|
|
196
199
|
return new WebGPUQuerySet(this, props);
|
|
197
200
|
}
|
|
198
201
|
|
|
202
|
+
override createFence(): WebGPUFence {
|
|
203
|
+
return new WebGPUFence(this);
|
|
204
|
+
}
|
|
205
|
+
|
|
199
206
|
createCanvasContext(props: CanvasContextProps): WebGPUCanvasContext {
|
|
200
207
|
return new WebGPUCanvasContext(this, this.adapter, props);
|
|
201
208
|
}
|
|
@@ -278,6 +285,14 @@ export class WebGPUDevice extends Device {
|
|
|
278
285
|
features.add('texture-compression-bc5-webgl');
|
|
279
286
|
}
|
|
280
287
|
|
|
288
|
+
if (this.handle.features.has('chromium-experimental-norm16-texture-formats')) {
|
|
289
|
+
features.add('norm16-renderable-webgl');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (this.handle.features.has('chromium-experimental-snorm16-texture-formats')) {
|
|
293
|
+
features.add('snorm16-renderable-webgl');
|
|
294
|
+
}
|
|
295
|
+
|
|
281
296
|
const WEBGPU_ALWAYS_FEATURES: DeviceFeature[] = [
|
|
282
297
|
'timer-query-webgl',
|
|
283
298
|
'compilation-status-async-webgl',
|
package/src/index.ts
CHANGED
|
@@ -12,3 +12,6 @@ export {WebGPUBuffer} from './adapter/resources/webgpu-buffer';
|
|
|
12
12
|
export {WebGPUTexture} from './adapter/resources/webgpu-texture';
|
|
13
13
|
export {WebGPUSampler} from './adapter/resources/webgpu-sampler';
|
|
14
14
|
export {WebGPUShader} from './adapter/resources/webgpu-shader';
|
|
15
|
+
export {WebGPUFence} from './adapter/resources/webgpu-fence';
|
|
16
|
+
|
|
17
|
+
export {getShaderLayoutFromWGSL} from './wgsl/get-shader-layout-wgsl';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {AttributeShaderType, ShaderLayout, TextureBindingLayout, log} from '@luma.gl/core';
|
|
6
|
+
import {TypeInfo, VariableInfo, WgslReflect, ResourceType} from 'wgsl_reflect';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse a ShaderLayout from WGSL shader source code.
|
|
10
|
+
* @param source WGSL source code (can contain both @vertex and @fragment entry points)
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
export function getShaderLayoutFromWGSL(source: string): ShaderLayout {
|
|
14
|
+
const shaderLayout: ShaderLayout = {attributes: [], bindings: []};
|
|
15
|
+
|
|
16
|
+
let parsedWGSL: WgslReflect;
|
|
17
|
+
try {
|
|
18
|
+
parsedWGSL = parseWGSL(source);
|
|
19
|
+
} catch (error: any) {
|
|
20
|
+
log.error(error.message)();
|
|
21
|
+
return shaderLayout;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (const uniform of parsedWGSL.uniforms) {
|
|
25
|
+
const members = [];
|
|
26
|
+
// @ts-expect-error
|
|
27
|
+
for (const attribute of uniform.type?.members || []) {
|
|
28
|
+
members.push({
|
|
29
|
+
name: attribute.name,
|
|
30
|
+
type: getType(attribute.type)
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
shaderLayout.bindings.push({
|
|
35
|
+
type: 'uniform',
|
|
36
|
+
name: uniform.name,
|
|
37
|
+
group: uniform.group,
|
|
38
|
+
location: uniform.binding,
|
|
39
|
+
// @ts-expect-error TODO - unused for now but needs fixing
|
|
40
|
+
members
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const texture of parsedWGSL.textures) {
|
|
45
|
+
const bindingDeclaration: TextureBindingLayout = {
|
|
46
|
+
type: 'texture',
|
|
47
|
+
name: texture.name,
|
|
48
|
+
group: texture.group,
|
|
49
|
+
location: texture.binding,
|
|
50
|
+
...getTextureBindingFromReflect(texture)
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
shaderLayout.bindings.push(bindingDeclaration);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const sampler of parsedWGSL.samplers) {
|
|
57
|
+
shaderLayout.bindings.push({
|
|
58
|
+
type: 'sampler',
|
|
59
|
+
name: sampler.name,
|
|
60
|
+
group: sampler.group,
|
|
61
|
+
location: sampler.binding
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const vertex = parsedWGSL.entry.vertex[0]; // "main"
|
|
66
|
+
|
|
67
|
+
// Vertex shader inputs
|
|
68
|
+
const attributeCount = vertex?.inputs.length || 0; // inputs to "main"
|
|
69
|
+
for (let i = 0; i < attributeCount; i++) {
|
|
70
|
+
const wgslAttribute = vertex.inputs[i];
|
|
71
|
+
|
|
72
|
+
// locationType can be "builtin"
|
|
73
|
+
if (wgslAttribute.locationType === 'location') {
|
|
74
|
+
const type = getType(wgslAttribute.type);
|
|
75
|
+
|
|
76
|
+
shaderLayout.attributes.push({
|
|
77
|
+
name: wgslAttribute.name,
|
|
78
|
+
location: Number(wgslAttribute.location),
|
|
79
|
+
type
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return shaderLayout;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Get a valid shader attribute type string from a wgsl-reflect type */
|
|
87
|
+
function getType(type: TypeInfo | null): AttributeShaderType {
|
|
88
|
+
// @ts-expect-error WgslReflect type checks needed
|
|
89
|
+
return type?.format ? `${type.name}<${type.format.name}>` : type.name;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseWGSL(source: string): WgslReflect {
|
|
93
|
+
try {
|
|
94
|
+
return new WgslReflect(source);
|
|
95
|
+
} catch (error: any) {
|
|
96
|
+
if (error instanceof Error) {
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
let message = 'WGSL parse error';
|
|
100
|
+
if (typeof error === 'object' && error?.message) {
|
|
101
|
+
message += `: ${error.message} `;
|
|
102
|
+
}
|
|
103
|
+
if (typeof error === 'object' && error?.token) {
|
|
104
|
+
message += error.token.line || '';
|
|
105
|
+
}
|
|
106
|
+
throw new Error(message, {cause: error});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getTextureBindingFromReflect(
|
|
111
|
+
v: VariableInfo, // VariableInfo for a texture
|
|
112
|
+
opts?: {format?: GPUTextureFormat} // optional: if you know the runtime format
|
|
113
|
+
): {
|
|
114
|
+
viewDimension: GPUTextureViewDimension;
|
|
115
|
+
/** @note sampleType float vs unfilterable-float cannot be determined without checking texture format and features */
|
|
116
|
+
sampleType: GPUTextureSampleType;
|
|
117
|
+
multisampled: boolean;
|
|
118
|
+
} {
|
|
119
|
+
if (v.resourceType !== ResourceType.Texture) {
|
|
120
|
+
throw new Error('Not a texture binding');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const typeName = v.type.name; // e.g. "texture_2d", "texture_cube_array", "texture_multisampled_2d"
|
|
124
|
+
// @ts-expect-error v.type.format is not always defined
|
|
125
|
+
const component = v.type.format?.name as 'f32' | 'i32' | 'u32' | undefined;
|
|
126
|
+
|
|
127
|
+
// viewDimension
|
|
128
|
+
const viewDimension: GPUTextureViewDimension = typeName.includes('cube_array')
|
|
129
|
+
? 'cube-array'
|
|
130
|
+
: typeName.includes('cube')
|
|
131
|
+
? 'cube'
|
|
132
|
+
: typeName.includes('2d_array')
|
|
133
|
+
? '2d-array'
|
|
134
|
+
: typeName.includes('3d')
|
|
135
|
+
? '3d'
|
|
136
|
+
: typeName.includes('1d')
|
|
137
|
+
? '1d'
|
|
138
|
+
: '2d';
|
|
139
|
+
|
|
140
|
+
// multisampled
|
|
141
|
+
const multisampled = typeName === 'texture_multisampled_2d';
|
|
142
|
+
|
|
143
|
+
// sampleType
|
|
144
|
+
let sampleType: GPUTextureSampleType;
|
|
145
|
+
if (typeName.startsWith('texture_depth')) {
|
|
146
|
+
sampleType = 'depth';
|
|
147
|
+
} else if (component === 'i32') {
|
|
148
|
+
sampleType = 'sint';
|
|
149
|
+
} else if (component === 'u32') {
|
|
150
|
+
sampleType = 'uint';
|
|
151
|
+
} else {
|
|
152
|
+
sampleType = 'float'; // default to float
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {viewDimension, sampleType, multisampled};
|
|
156
|
+
}
|