@luma.gl/webgpu 9.0.0-alpha.10

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.
Files changed (128) hide show
  1. package/LICENSE +32 -0
  2. package/README.md +1 -0
  3. package/dist/adapter/helpers/accessor-to-format.d.ts +1 -0
  4. package/dist/adapter/helpers/accessor-to-format.d.ts.map +1 -0
  5. package/dist/adapter/helpers/accessor-to-format.js +2 -0
  6. package/dist/adapter/helpers/accessor-to-format.js.map +1 -0
  7. package/dist/adapter/helpers/convert-texture-format.d.ts +5 -0
  8. package/dist/adapter/helpers/convert-texture-format.d.ts.map +1 -0
  9. package/dist/adapter/helpers/convert-texture-format.js +8 -0
  10. package/dist/adapter/helpers/convert-texture-format.js.map +1 -0
  11. package/dist/adapter/helpers/generate-mipmaps.d.ts +10 -0
  12. package/dist/adapter/helpers/generate-mipmaps.d.ts.map +1 -0
  13. package/dist/adapter/helpers/generate-mipmaps.js +95 -0
  14. package/dist/adapter/helpers/generate-mipmaps.js.map +1 -0
  15. package/dist/adapter/helpers/get-bind-group.d.ts +13 -0
  16. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -0
  17. package/dist/adapter/helpers/get-bind-group.js +60 -0
  18. package/dist/adapter/helpers/get-bind-group.js.map +1 -0
  19. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +12 -0
  20. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -0
  21. package/dist/adapter/helpers/get-vertex-buffer-layout.js +98 -0
  22. package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -0
  23. package/dist/adapter/helpers/webgpu-parameters.d.ts +9 -0
  24. package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -0
  25. package/dist/adapter/helpers/webgpu-parameters.js +143 -0
  26. package/dist/adapter/helpers/webgpu-parameters.js.map +1 -0
  27. package/dist/adapter/resources/webgpu-buffer.d.ts +17 -0
  28. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -0
  29. package/dist/adapter/resources/webgpu-buffer.js +80 -0
  30. package/dist/adapter/resources/webgpu-buffer.js.map +1 -0
  31. package/dist/adapter/resources/webgpu-command-encoder.d.ts +44 -0
  32. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -0
  33. package/dist/adapter/resources/webgpu-command-encoder.js +66 -0
  34. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -0
  35. package/dist/adapter/resources/webgpu-compute-pass.d.ts +32 -0
  36. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -0
  37. package/dist/adapter/resources/webgpu-compute-pass.js +58 -0
  38. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -0
  39. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +12 -0
  40. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -0
  41. package/dist/adapter/resources/webgpu-compute-pipeline.js +27 -0
  42. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -0
  43. package/dist/adapter/resources/webgpu-external-texture.d.ts +18 -0
  44. package/dist/adapter/resources/webgpu-external-texture.d.ts.map +1 -0
  45. package/dist/adapter/resources/webgpu-external-texture.js +30 -0
  46. package/dist/adapter/resources/webgpu-external-texture.js.map +1 -0
  47. package/dist/adapter/resources/webgpu-framebuffer.d.ts +29 -0
  48. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -0
  49. package/dist/adapter/resources/webgpu-framebuffer.js +111 -0
  50. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -0
  51. package/dist/adapter/resources/webgpu-query.d.ts +1 -0
  52. package/dist/adapter/resources/webgpu-query.d.ts.map +1 -0
  53. package/dist/adapter/resources/webgpu-query.js +2 -0
  54. package/dist/adapter/resources/webgpu-query.js.map +1 -0
  55. package/dist/adapter/resources/webgpu-render-pass.d.ts +34 -0
  56. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -0
  57. package/dist/adapter/resources/webgpu-render-pass.js +98 -0
  58. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -0
  59. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +44 -0
  60. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -0
  61. package/dist/adapter/resources/webgpu-render-pipeline.js +164 -0
  62. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -0
  63. package/dist/adapter/resources/webgpu-sampler.d.ts +16 -0
  64. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -0
  65. package/dist/adapter/resources/webgpu-sampler.js +25 -0
  66. package/dist/adapter/resources/webgpu-sampler.js.map +1 -0
  67. package/dist/adapter/resources/webgpu-shader.d.ts +21 -0
  68. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -0
  69. package/dist/adapter/resources/webgpu-shader.js +64 -0
  70. package/dist/adapter/resources/webgpu-shader.js.map +1 -0
  71. package/dist/adapter/resources/webgpu-texture.d.ts +39 -0
  72. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -0
  73. package/dist/adapter/resources/webgpu-texture.js +111 -0
  74. package/dist/adapter/resources/webgpu-texture.js.map +1 -0
  75. package/dist/adapter/webgpu-canvas-context.d.ts +32 -0
  76. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -0
  77. package/dist/adapter/webgpu-canvas-context.js +95 -0
  78. package/dist/adapter/webgpu-canvas-context.js.map +1 -0
  79. package/dist/adapter/webgpu-device.d.ts +67 -0
  80. package/dist/adapter/webgpu-device.d.ts.map +1 -0
  81. package/dist/adapter/webgpu-device.js +242 -0
  82. package/dist/adapter/webgpu-device.js.map +1 -0
  83. package/dist/adapter/webgpu-types.d.ts +1 -0
  84. package/dist/adapter/webgpu-types.d.ts.map +1 -0
  85. package/dist/adapter/webgpu-types.js +2 -0
  86. package/dist/adapter/webgpu-types.js.map +1 -0
  87. package/dist/bundle.d.ts +2 -0
  88. package/dist/bundle.d.ts.map +1 -0
  89. package/dist/bundle.js +5 -0
  90. package/dist/bundle.js.map +1 -0
  91. package/dist/glsl/glsllang.d.ts +3 -0
  92. package/dist/glsl/glsllang.d.ts.map +1 -0
  93. package/dist/glsl/glsllang.js +10 -0
  94. package/dist/glsl/glsllang.js.map +1 -0
  95. package/dist/index.d.ts +8 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +8 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/init.d.ts +2 -0
  100. package/dist/init.d.ts.map +1 -0
  101. package/dist/init.js +4 -0
  102. package/dist/init.js.map +1 -0
  103. package/package.json +36 -0
  104. package/src/adapter/helpers/accessor-to-format.ts +101 -0
  105. package/src/adapter/helpers/convert-texture-format.ts +10 -0
  106. package/src/adapter/helpers/generate-mipmaps.ts +107 -0
  107. package/src/adapter/helpers/get-bind-group.ts +82 -0
  108. package/src/adapter/helpers/get-vertex-buffer-layout.ts +123 -0
  109. package/src/adapter/helpers/webgpu-parameters.ts +229 -0
  110. package/src/adapter/resources/webgpu-buffer.ts +92 -0
  111. package/src/adapter/resources/webgpu-command-encoder.ts +112 -0
  112. package/src/adapter/resources/webgpu-compute-pass.ts +74 -0
  113. package/src/adapter/resources/webgpu-compute-pipeline.ts +34 -0
  114. package/src/adapter/resources/webgpu-external-texture.ts +37 -0
  115. package/src/adapter/resources/webgpu-framebuffer.ts +120 -0
  116. package/src/adapter/resources/webgpu-query.ts +43 -0
  117. package/src/adapter/resources/webgpu-render-pass.ts +131 -0
  118. package/src/adapter/resources/webgpu-render-pipeline.ts +246 -0
  119. package/src/adapter/resources/webgpu-sampler.ts +32 -0
  120. package/src/adapter/resources/webgpu-shader.ts +72 -0
  121. package/src/adapter/resources/webgpu-texture.ts +244 -0
  122. package/src/adapter/webgpu-canvas-context.ts +102 -0
  123. package/src/adapter/webgpu-device.ts +294 -0
  124. package/src/adapter/webgpu-types.ts +0 -0
  125. package/src/bundle.ts +4 -0
  126. package/src/glsl/glsllang.ts +14 -0
  127. package/src/index.ts +13 -0
  128. package/src/init.ts +4 -0
@@ -0,0 +1,244 @@
1
+ // luma.gl, MIT license
2
+ import {Texture, TextureProps, Sampler, SamplerProps, assert} from '@luma.gl/api';
3
+ import {getWebGPUTextureFormat} from '../helpers/convert-texture-format';
4
+ import type WebGPUDevice from '../webgpu-device';
5
+ import WebGPUSampler from './webgpu-sampler';
6
+
7
+ const BASE_DIMENSIONS: Record<string, '1d' | '2d' | '3d'> = {
8
+ '1d': '1d',
9
+ '2d': '2d',
10
+ '2d-array': '2d',
11
+ 'cube': '2d',
12
+ 'cube-array': '2d',
13
+ '3d': '3d'
14
+ };
15
+
16
+ export default class WebGPUTexture extends Texture {
17
+ readonly device: WebGPUDevice;
18
+ readonly handle: GPUTexture;
19
+ readonly view: GPUTextureView;
20
+ sampler: WebGPUSampler = null;
21
+
22
+ // static async createFromImageURL(src, usage = 0) {
23
+ // const img = document.createElement('img');
24
+ // img.src = src;
25
+ // await img.decode();
26
+ // return WebGPUTexture(img, usage);
27
+ // }
28
+
29
+ constructor(device: WebGPUDevice, props: TextureProps) {
30
+ super(device, props);
31
+
32
+ if (typeof this.props.format === 'number') {
33
+ throw new Error('number format');
34
+ }
35
+
36
+ this.device = device;
37
+ this.handle = this.props.handle || this.createHandle();
38
+
39
+ if (this.props.data) {
40
+ this.setData({data: this.props.data} );
41
+ }
42
+
43
+ // Create a default sampler. This mimics the WebGL1 API where sampler props are stored on the texture
44
+ // this.setSampler(props.sampler);
45
+ this.sampler = props.sampler instanceof WebGPUSampler ? props.sampler : new WebGPUSampler(this.device, props.sampler);
46
+
47
+ // TODO - To support texture arrays we need to create custom views...
48
+ // But we are not ready to expose TextureViews to the public API.
49
+ this.view = this.handle.createView({
50
+ // format: this.props.format,
51
+ // dimension: this.props.dimension,
52
+ // aspect = "all";
53
+ // baseMipLevel: 0;
54
+ // mipLevelCount;
55
+ // baseArrayLayer = 0;
56
+ // arrayLayerCount;
57
+ });
58
+ }
59
+
60
+ protected createHandle(): GPUTexture {
61
+ if (typeof this.props.format === 'number') {
62
+ throw new Error('number format');
63
+ }
64
+
65
+ // Deduce size from data - TODO this is a hack
66
+ // @ts-expect-error
67
+ const width = this.props.width || this.props.data?.width || 1;
68
+ // @ts-expect-error
69
+ const height = this.props.height || this.props.data?.height || 1;
70
+
71
+ return this.device.handle.createTexture({
72
+ size: {
73
+ width,
74
+ height,
75
+ depthOrArrayLayers: this.props.depth
76
+ },
77
+ dimension: BASE_DIMENSIONS[this.props.dimension],
78
+ format: getWebGPUTextureFormat(this.props.format),
79
+ usage: this.props.usage,
80
+ mipLevelCount: this.props.mipLevels,
81
+ sampleCount: this.props.samples
82
+ });
83
+ }
84
+
85
+ destroy(): void {
86
+ this.handle.destroy();
87
+ }
88
+
89
+ /**
90
+ * Set default sampler
91
+ * Accept a sampler instance or set of props;
92
+ */
93
+ setSampler(sampler: Sampler | SamplerProps): this {
94
+ this.sampler = sampler instanceof WebGPUSampler ? sampler : new WebGPUSampler(this.device, sampler);
95
+ return this;
96
+ }
97
+
98
+ setData(options: {
99
+ data: any;
100
+ }) {
101
+ return this.setImage({source: options.data});
102
+ }
103
+
104
+ /** Set image */
105
+ setImage(options: {
106
+ source: ImageBitmap | HTMLCanvasElement | OffscreenCanvas;
107
+ width?: number;
108
+ height?: number;
109
+ depth?: number;
110
+ sourceX?: number;
111
+ sourceY?: number;
112
+ mipLevel?: number;
113
+ x?: number;
114
+ y?: number;
115
+ z?: number;
116
+ aspect?: 'all' | 'stencil-only' | 'depth-only';
117
+ colorSpace?: 'srgb';
118
+ premultipliedAlpha?: boolean;
119
+ }): this {
120
+ const {
121
+ source,
122
+ width = options.source.width,
123
+ height = options.source.height,
124
+ depth = 1,
125
+ sourceX = 0,
126
+ sourceY = 0,
127
+ mipLevel = 0,
128
+ x = 0,
129
+ y = 0,
130
+ z = 0,
131
+ aspect = 'all',
132
+ colorSpace = 'srgb',
133
+ premultipliedAlpha = false
134
+ } = options;
135
+
136
+ // TODO - max out width
137
+
138
+ this.device.handle.queue.copyExternalImageToTexture(
139
+ // source: GPUImageCopyExternalImage
140
+ {
141
+ source,
142
+ origin: [sourceX, sourceY]
143
+ },
144
+ // destination: GPUImageCopyTextureTagged
145
+ {
146
+ texture: this.handle,
147
+ origin: [x, y, z],
148
+ mipLevel,
149
+ aspect,
150
+ colorSpace,
151
+ premultipliedAlpha
152
+ },
153
+ // copySize: GPUExtent3D
154
+ [
155
+ width,
156
+ height,
157
+ depth
158
+ ]
159
+ );
160
+ return this;
161
+ }
162
+
163
+ /*
164
+ async readPixels() {
165
+ const readbackBuffer = device.createBuffer({
166
+ usage: Buffer.COPY_DST | Buffer.MAP_READ,
167
+ size: 4 * textureWidth * textureHeight,
168
+ });
169
+
170
+ // Copy data from the texture to the buffer.
171
+ const encoder = device.createCommandEncoder();
172
+ encoder.copyTextureToBuffer(
173
+ { texture },
174
+ { buffer, rowPitch: textureWidth * 4 },
175
+ [textureWidth, textureHeight],
176
+ );
177
+ device.submit([encoder.finish()]);
178
+
179
+ // Get the data on the CPU.
180
+ await buffer.mapAsync(GPUMapMode.READ);
181
+ saveScreenshot(buffer.getMappedRange());
182
+ buffer.unmap();
183
+ }
184
+
185
+ setImageData(imageData, usage): this {
186
+ let data = null;
187
+
188
+ const bytesPerRow = Math.ceil((img.width * 4) / 256) * 256;
189
+ if (bytesPerRow == img.width * 4) {
190
+ data = imageData.data;
191
+ } else {
192
+ data = new Uint8Array(bytesPerRow * img.height);
193
+ let imagePixelIndex = 0;
194
+ for (let y = 0; y < img.height; ++y) {
195
+ for (let x = 0; x < img.width; ++x) {
196
+ const i = x * 4 + y * bytesPerRow;
197
+ data[i] = imageData.data[imagePixelIndex];
198
+ data[i + 1] = imageData.data[imagePixelIndex + 1];
199
+ data[i + 2] = imageData.data[imagePixelIndex + 2];
200
+ data[i + 3] = imageData.data[imagePixelIndex + 3];
201
+ imagePixelIndex += 4;
202
+ }
203
+ }
204
+ }
205
+ return this;
206
+ }
207
+
208
+ setData(data): this {
209
+ const textureDataBuffer = this.device.handle.createBuffer({
210
+ size: data.byteLength,
211
+ usage: Buffer.COPY_DST | Buffer.COPY_SRC,
212
+ mappedAtCreation: true
213
+ });
214
+ new Uint8Array(textureDataBuffer.getMappedRange()).set(data);
215
+ textureDataBuffer.unmap();
216
+
217
+ this.setBuffer(textureDataBuffer);
218
+
219
+ textureDataBuffer.destroy();
220
+ return this;
221
+ }
222
+
223
+ setBuffer(textureDataBuffer, {bytesPerRow}): this {
224
+ const commandEncoder = this.device.handle.createCommandEncoder();
225
+ commandEncoder.copyBufferToTexture(
226
+ {
227
+ buffer: textureDataBuffer,
228
+ bytesPerRow
229
+ },
230
+ {
231
+ texture: this.handle
232
+ },
233
+ {
234
+ width,
235
+ height,
236
+ depth
237
+ }
238
+ );
239
+
240
+ this.device.handle.defaultQueue.submit([commandEncoder.finish()]);
241
+ return this;
242
+ }
243
+ */
244
+ }
@@ -0,0 +1,102 @@
1
+ import type {Texture, TextureFormat, CanvasContextProps} from '@luma.gl/api';
2
+ import {CanvasContext, log} from '@luma.gl/api';
3
+ import {getWebGPUTextureFormat} from './helpers/convert-texture-format';
4
+ import WebGPUDevice from './webgpu-device';
5
+ import WEBGPUFramebuffer from './resources/webgpu-framebuffer';
6
+
7
+ /**
8
+ * Holds a WebGPU Canvas Context which handles resizing etc
9
+ */
10
+ export default class WebGPUCanvasContext extends CanvasContext {
11
+ readonly device: WebGPUDevice;
12
+ readonly gpuCanvasContext: GPUCanvasContext;
13
+ readonly format: TextureFormat;
14
+ width: number = -1;
15
+ height: number = -1;
16
+ depthStencilFormat: TextureFormat = 'depth24plus';
17
+ sampleCount: number = 1;
18
+
19
+ private depthStencilAttachment: Texture | null = null;
20
+
21
+ constructor(device: WebGPUDevice, adapter: GPUAdapter, props: CanvasContextProps) {
22
+ super(props);
23
+ this.device = device;
24
+ this.gpuCanvasContext = this.canvas.getContext('webgpu') as GPUCanvasContext;
25
+ this.format = this.gpuCanvasContext.getPreferredFormat(adapter);
26
+ }
27
+
28
+ destroy() {
29
+ this.gpuCanvasContext.unconfigure();
30
+ }
31
+
32
+ /** Update framebuffer with properly resized "swap chain" texture views */
33
+ getCurrentFramebuffer(): WEBGPUFramebuffer {
34
+ // Ensure the canvas context size is updated
35
+ this.update();
36
+
37
+ // Wrap the current canvas context texture in a luma.gl texture
38
+ const currentColorAttachment = this.device.createTexture({
39
+ id: 'default-render-target',
40
+ handle: this.gpuCanvasContext.getCurrentTexture(),
41
+ format: this.format,
42
+ width: this.width,
43
+ height: this.height
44
+ });
45
+
46
+ // Resize the depth stencil attachment
47
+ this._createDepthStencilAttachment();
48
+
49
+ return new WEBGPUFramebuffer(this.device, {
50
+ colorAttachments: [currentColorAttachment],
51
+ depthStencilAttachment: this.depthStencilAttachment
52
+ });
53
+ }
54
+
55
+ /** Resizes and updates render targets if necessary */
56
+ update() {
57
+ const [width, height] = this.getPixelSize();
58
+
59
+ const sizeChanged = width !== this.width || height !== this.height;
60
+
61
+ if (sizeChanged) {
62
+ this.width = width;
63
+ this.height = height;
64
+
65
+ if (this.depthStencilAttachment) {
66
+ this.depthStencilAttachment.destroy();
67
+ this.depthStencilAttachment = null;
68
+ }
69
+
70
+ // Reconfigure the canvas size.
71
+ // https://www.w3.org/TR/webgpu/#canvas-configuration
72
+ this.gpuCanvasContext.configure({
73
+ device: this.device.handle,
74
+ format: getWebGPUTextureFormat(this.format),
75
+ size: [this.width, this.height],
76
+ colorSpace: this.props.colorSpace,
77
+ compositingAlphaMode: this.props.compositingAlphaMode
78
+ });
79
+
80
+ log.log(1, `Resized to ${this.width}x${this.height}px`)();
81
+ }
82
+
83
+ }
84
+
85
+ resize(options?: {width?: number; height?: number; useDevicePixels?: boolean | number}): void {
86
+ this.update();
87
+ }
88
+
89
+ /** We build render targets on demand (i.e. not when size changes but when about to render) */
90
+ _createDepthStencilAttachment() {
91
+ if (!this.depthStencilAttachment) {
92
+ this.depthStencilAttachment = this.device.createTexture({
93
+ id: 'depth-stencil-target',
94
+ format: this.depthStencilFormat,
95
+ width: this.width,
96
+ height: this.height,
97
+ usage: GPUTextureUsage.RENDER_ATTACHMENT
98
+ });
99
+ }
100
+ return this.depthStencilAttachment;
101
+ }
102
+ }
@@ -0,0 +1,294 @@
1
+ /// <reference types="@webgpu/types" />
2
+
3
+ import type {
4
+ DeviceProps,
5
+ DeviceInfo,
6
+ DeviceLimits,
7
+ DeviceFeature,
8
+ CanvasContextProps,
9
+ BufferProps,
10
+ SamplerProps,
11
+ ShaderProps,
12
+ TextureProps,
13
+ TextureFormat,
14
+ ExternalTextureProps,
15
+ FramebufferProps,
16
+ RenderPipelineProps,
17
+ ComputePipelineProps,
18
+ RenderPassProps,
19
+ ComputePassProps
20
+ } from '@luma.gl/api';
21
+ import {Device, CanvasContext, log, cast} from '@luma.gl/api';
22
+ import WebGPUBuffer from './resources/webgpu-buffer';
23
+ import WebGPUTexture from './resources/webgpu-texture';
24
+ import WebGPUExternalTexture from './resources/webgpu-external-texture';
25
+ import WebGPUSampler from './resources/webgpu-sampler';
26
+ import WebGPUShader from './resources/webgpu-shader';
27
+ import WebGPURenderPipeline from './resources/webgpu-render-pipeline';
28
+ import WebGPUFramebuffer from './resources/webgpu-framebuffer';
29
+ import WebGPUComputePipeline from './resources/webgpu-compute-pipeline';
30
+ import WebGPURenderPass from './resources/webgpu-render-pass';
31
+ import WebGPUComputePass from './resources/webgpu-compute-pass';
32
+
33
+ import WebGPUCanvasContext from './webgpu-canvas-context';
34
+ // import {loadGlslangModule} from '../glsl/glslang';
35
+
36
+ /** WebGPU Device implementation */
37
+ export default class WebGPUDevice extends Device {
38
+ readonly handle: GPUDevice;
39
+ readonly adapter: GPUAdapter;
40
+ readonly lost: Promise<{reason: 'destroyed'; message: string}>;
41
+ canvasContext: WebGPUCanvasContext | null = null;
42
+
43
+ commandEncoder: GPUCommandEncoder | null = null;
44
+ renderPass: WebGPURenderPass | null = null;
45
+
46
+ private _info: DeviceInfo;
47
+ private _isLost: boolean = false;
48
+
49
+ static type: string = 'webgpu';
50
+
51
+ /** Check if WebGPU is available */
52
+ static isSupported(): boolean {
53
+ return Boolean(typeof navigator !== 'undefined' && navigator.gpu);
54
+ }
55
+
56
+ static async create(props: DeviceProps): Promise<WebGPUDevice> {
57
+ if (!navigator.gpu) {
58
+ throw new Error(
59
+ 'WebGPU not available. Open in Chrome Canary and turn on chrome://flags/#enable-unsafe-webgpu'
60
+ );
61
+ }
62
+ log.groupCollapsed(1, 'WebGPUDevice created')();
63
+ const adapter = await navigator.gpu.requestAdapter({
64
+ powerPreference: 'high-performance'
65
+ // forceSoftware: false
66
+ });
67
+ if (!adapter) {
68
+ throw new Error('Failed to request WebGPU adapter');
69
+ }
70
+
71
+ log.probe(1, 'Adapter available')();
72
+
73
+ const gpuDevice = await adapter.requestDevice({
74
+ requiredFeatures: adapter.features as ReadonlySet<GPUFeatureName>
75
+ // TODO ensure we obtain best limits
76
+ // requiredLimits: adapter.limits
77
+ });
78
+ log.probe(1, 'GPUDevice available')();
79
+
80
+ if (typeof props.canvas === 'string') {
81
+ await CanvasContext.pageLoaded;
82
+ log.probe(1, 'DOM is loaded')();
83
+ }
84
+
85
+ const device = new WebGPUDevice(gpuDevice, adapter, props);
86
+ log.probe(1, 'Device created', device.info)();
87
+ log.table(1, device.info)();
88
+ log.groupEnd(1)();
89
+ return device;
90
+ }
91
+
92
+ constructor(device: GPUDevice, adapter: GPUAdapter, props: DeviceProps) {
93
+ super(props);
94
+ this.handle = device;
95
+ this.adapter = adapter;
96
+
97
+ this._info = {
98
+ type: 'webgpu',
99
+ vendor: this.adapter.name,
100
+ renderer: '',
101
+ version: '',
102
+ gpu: 'unknown', // 'nvidia' | 'amd' | 'intel' | 'apple' | 'unknown',
103
+ shadingLanguages: ['glsl', 'wgsl'],
104
+ shadingLanguageVersions: {
105
+ glsl: '450',
106
+ wgsl: '100'
107
+ },
108
+ vendorMasked: '',
109
+ rendererMasked: ''
110
+ };
111
+
112
+ // "Context" loss handling
113
+ this.lost = new Promise<{reason: 'destroyed'; message: string}>(async (resolve) => {
114
+ const lostInfo = await this.handle.lost;
115
+ this._isLost = true;
116
+ resolve({reason: 'destroyed', message: lostInfo.message});
117
+ });
118
+
119
+ // Note: WebGPU devices can be created without a canvas, for compute shader purposes
120
+ if (props.canvas) {
121
+ this.canvasContext = new WebGPUCanvasContext(this, this.adapter, {canvas: props.canvas});
122
+ }
123
+
124
+ this.features = this._getFeatures();
125
+ }
126
+
127
+ // TODO
128
+ // Load the glslang module now so that it is available synchronously when compiling shaders
129
+ // const {glsl = true} = props;
130
+ // this.glslang = glsl && await loadGlslangModule();
131
+
132
+ destroy() {
133
+ this.handle.destroy();
134
+ }
135
+
136
+ get info(): DeviceInfo {
137
+ return this._info;
138
+ }
139
+
140
+ features: Set<DeviceFeature>;
141
+
142
+ get limits(): DeviceLimits {
143
+ return this.handle.limits;
144
+ }
145
+
146
+ isTextureFormatSupported(format: TextureFormat): boolean {
147
+ return !format.includes('webgl');
148
+ }
149
+
150
+ /** @todo implement proper check? */
151
+ isTextureFormatFilterable(format: TextureFormat): boolean {
152
+ return this.isTextureFormatSupported(format);
153
+ }
154
+
155
+ /** @todo implement proper check? */
156
+ isTextureFormatRenderable(format: TextureFormat): boolean {
157
+ return this.isTextureFormatSupported(format);
158
+ }
159
+
160
+ get isLost(): boolean {
161
+ return this._isLost;
162
+ }
163
+
164
+ _createBuffer(props: BufferProps): WebGPUBuffer {
165
+ return new WebGPUBuffer(this, props);
166
+ }
167
+
168
+ _createTexture(props: TextureProps): WebGPUTexture {
169
+ return new WebGPUTexture(this, props);
170
+ }
171
+
172
+ createExternalTexture(props: ExternalTextureProps): WebGPUExternalTexture {
173
+ return new WebGPUExternalTexture(this, props);
174
+ }
175
+
176
+ createShader(props: ShaderProps): WebGPUShader {
177
+ return new WebGPUShader(this, props);
178
+ }
179
+
180
+ createSampler(props: SamplerProps): WebGPUSampler {
181
+ return new WebGPUSampler(this, props);
182
+ }
183
+
184
+ createRenderPipeline(props: RenderPipelineProps): WebGPURenderPipeline {
185
+ return new WebGPURenderPipeline(this, props);
186
+ }
187
+
188
+ createFramebuffer(props: FramebufferProps): WebGPUFramebuffer {
189
+ throw new Error('Not implemented');
190
+ }
191
+
192
+ createComputePipeline(props: ComputePipelineProps): WebGPUComputePipeline {
193
+ return new WebGPUComputePipeline(this, props);
194
+ }
195
+
196
+ // WebGPU specifics
197
+
198
+ /**
199
+ * Allows a render pass to begin against a canvas context
200
+ * @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
201
+ */
202
+ beginRenderPass(props: RenderPassProps): WebGPURenderPass {
203
+ this.commandEncoder = this.commandEncoder || this.handle.createCommandEncoder();
204
+ return new WebGPURenderPass(this, props);
205
+ }
206
+
207
+ beginComputePass(props: ComputePassProps): WebGPUComputePass {
208
+ this.commandEncoder = this.commandEncoder || this.handle.createCommandEncoder();
209
+ return new WebGPUComputePass(this, props);
210
+ }
211
+
212
+ createCanvasContext(props: CanvasContextProps): WebGPUCanvasContext {
213
+ return new WebGPUCanvasContext(this, this.adapter, props);
214
+ }
215
+
216
+ /**
217
+ * Gets default renderpass encoder.
218
+ * Creates a new encoder against default canvasContext if not already created
219
+ * @note Called internally by Model.
220
+ */
221
+ getDefaultRenderPass(): WebGPURenderPass {
222
+ this.renderPass =
223
+ this.renderPass ||
224
+ this.beginRenderPass({
225
+ framebuffer: this.canvasContext?.getCurrentFramebuffer()
226
+ });
227
+ return this.renderPass;
228
+ }
229
+
230
+ submit(): void {
231
+ this.renderPass?.endPass();
232
+ const commandBuffer = this.commandEncoder?.finish();
233
+ if (commandBuffer) {
234
+ this.handle.queue.submit([commandBuffer]);
235
+ }
236
+ this.commandEncoder = null;
237
+ this.renderPass = null;
238
+ }
239
+
240
+ _getFeatures() {
241
+ // WebGPU Features
242
+ const features = new Set<DeviceFeature>(this.handle.features as Set<DeviceFeature>);
243
+
244
+ // Fixups for pre-standard names: https://github.com/webgpu-native/webgpu-headers/issues/133
245
+ // @ts-expect-error Chrome Canary v99
246
+ if (features.has('depth-clamping')) {
247
+ // @ts-expect-error Chrome Canary v99
248
+ features.delete('depth-clamping');
249
+ features.add('depth-clip-control');
250
+ }
251
+
252
+ // Add subsets
253
+ if (features.has('texture-compression-bc')) {
254
+ features.add('texture-compression-bc5-webgl');
255
+ }
256
+
257
+ features.add('webgpu');
258
+
259
+ features.add('timer-query-webgl');
260
+
261
+ // WEBGL1 SUPPORT
262
+ features.add('vertex-array-object-webgl1');
263
+ features.add('instanced-rendering-webgl1');
264
+ features.add('multiple-render-targets-webgl1');
265
+ features.add('index-uint32-webgl1');
266
+ features.add('blend-minmax-webgl1');
267
+ features.add('texture-blend-float-webgl1');
268
+
269
+ // TEXTURES, RENDERBUFFERS
270
+ features.add('texture-formats-srgb-webgl1');
271
+
272
+ // TEXTURES
273
+ features.add('texture-formats-depth-webgl1');
274
+ features.add('texture-formats-float32-webgl1');
275
+ features.add('texture-formats-float16-webgl1');
276
+
277
+ features.add('texture-filter-linear-float32-webgl');
278
+ features.add('texture-filter-linear-float16-webgl');
279
+ features.add('texture-filter-anisotropic-webgl');
280
+
281
+ // FRAMEBUFFERS, TEXTURES AND RENDERBUFFERS
282
+ features.add('texture-renderable-rgba32float-webgl');
283
+ features.add('texture-renderable-float32-webgl');
284
+ features.add('texture-renderable-float16-webgl');
285
+
286
+ // GLSL extensions
287
+ features.add('glsl-frag-data');
288
+ features.add('glsl-frag-depth');
289
+ features.add('glsl-derivatives');
290
+ features.add('glsl-texture-lod');
291
+
292
+ return features;
293
+ }
294
+ }
File without changes
package/src/bundle.ts ADDED
@@ -0,0 +1,4 @@
1
+ // @ts-nocheck
2
+ const moduleExports = require('./index');
3
+ globalThis.luma = globalThis.luma || {};
4
+ module.exports = Object.assign(globalThis.luma, moduleExports);
@@ -0,0 +1,14 @@
1
+ // Inspired by webgpu samples at https://github.com/austinEng/webgpu-samples/blob/master/src/glslang.ts
2
+ // under BSD 3-clause license
3
+
4
+ let glslang: unknown;
5
+
6
+ /** Dynamically load the GLSL compiler */
7
+ export async function loadGlslangModule() {
8
+ if (!glslang) {
9
+ // @ts-ignore
10
+ const glslangModule = await import(/* webpackIgnore: true */ 'https://unpkg.com/@webgpu/glslang@0.0.15/dist/web-devel/glslang.js');
11
+ glslang = await glslangModule.default();
12
+ }
13
+ return glslang;
14
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+
2
+ // Initialize any global state
3
+ import '@luma.gl/api';
4
+ import './init'
5
+
6
+ // WEBGPU ADAPTER
7
+ export {default as WebGPUDevice} from './adapter/webgpu-device';
8
+
9
+ // WEBGPU CLASSES (typically not accessed directly)
10
+ export {default as WebGPUBuffer} from './adapter/resources/webgpu-buffer';
11
+ export {default as WebGPUTexture} from './adapter/resources/webgpu-texture';
12
+ export {default as WebGPUSampler} from './adapter/resources/webgpu-sampler';
13
+ export {default as WebGPUShader} from './adapter/resources/webgpu-shader';
package/src/init.ts ADDED
@@ -0,0 +1,4 @@
1
+ import {luma} from '@luma.gl/api';
2
+ import WebGPUDevice from './adapter/webgpu-device';
3
+
4
+ luma.registerDevices([WebGPUDevice]);